Import Upstream version 0.14.0

This commit is contained in:
Lu zhiping 2022-06-27 18:02:21 +08:00
commit 6db9427a94
81 changed files with 71670 additions and 0 deletions

15
COPYING Normal file
View File

@ -0,0 +1,15 @@
ISC License
Copyright (C) 2006-2016 libass contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

353
Changelog Normal file
View File

@ -0,0 +1,353 @@
libass (0.14.0)
* Brand new, faster and better outline stroker (replaces FreeType stroker)
* Remove option to use the FreeType rasterizer
* Fix spots of missing border around self-intersecting shapes
* Switch from Yasm to NASM for building hand-written assembler code
* Support Core Text font provider on Mac OS X 10.6 and 10.7
* Clear font cache in ass_set_fonts(). This fixes potentially incorrect
font choices and an occasional crash if this function is called midway
through rendering a file.
libass (0.13.7)
* Fix invalid memory accesses with BorderStyle=4
* Fix change detection bug on frame resizes
* Fix cache bugs with border size
* Reduce precision of border width in font outline caching
* Don't treat :;<=>? as hexadecimal digits in color headers
* Fix parsing of unusual Alignment values in ASS style definitions
* Fix potential truncation in timestamp parsing
* Treat negative PlayResX/PlayResY like VSFilter
* Fixes to parsing of embedded fonts
* Remove arbitrary bitmap limit (fixes issues with subtitle rendering at 4K)
* Allow using shadow offset to adjust size of background with BorderStyle=4
* Fix TrueType/OpenType font collection handling with the DirectWrite backend
libass (0.13.6)
* Bump ABI version to account for the new Justify field in ASS_Style
that was added in 0.13.5.
libass (0.13.5)
* Add Justify style override that changes text justification
(left/right/center) without affecting event positioning.
* Fix ass_set_cache_limits() to affect total bitmap cache size
including composite bitmap cache.
* Number parsing fixes
* Fix illegal read when parsing some numbers in scientific notation
with huge exponents.
* Correctly evaluate numbers in scientific notation with large exponents.
* Correctly evaluate numbers with many leading zeros.
* Bug fixes found with fuzzing
* Fix a small memory leak in the parser.
* Fix illegal read in the parser on specially crafted input with \t tags.
libass (0.13.4)
* Bug fixes found with fuzzing
* Fix memory reallocation in the shaper. (CVE-2016-7972)
* Fix two small memory leaks in the parser and test program.
* Fix illegal read in Gaussian blur coefficient calculations.
(CVE-2016-7970)
* Fix mode 0/3 line wrapping equalization in specific cases which could
result in illegal reads while laying out and shaping text.
(CVE-2016-7969)
libass (0.13.3)
* Improve compatibility/portablility of build system,
should fix e.g. compilation on Solaris.
* Fix memory leak in DirectWrite font provider.
* Fix the rasterizer when rendering some large outlines.
* Remove hack that forced RTL base direction depending on font encoding.
* Greatly improve the internal caches with refcounting and gradual clearing.
libass (0.13.2)
* Add ass_set_check_readorder() API function to disable use of the ReadOrder
field for duplicate checking in ass_process_chunk().
* ass_step_sub(track, now, 0) now finds the start of the subtitle at "now".
* Bug fixes
* Fix an issue with the new duplicate checking, which could lead to
missing subtitles after seeking.
* Fix a crash with CoreText under specific circumstances
libass (0.13.1)
* Much faster duplicate event check in ass_process_chunk. This can
bring a large speedup for embedded subtitles with lots of events.
To make this possible, it is now forbidden to combine calls to
ass_process_chunk with any other method of manipulating the event
list, namely, manual manipulation and calls to ass_process_data,
ass_read_memory and ass_read_file.
* Interpret negative timestamp components as actual negative numbers.
This change increases compatibility with VSFilter.
* Font matching changes for VSFilter compatibility
* Look up fonts that contain PostScript outlines by their PostScript
name instead of their full names. Family names continue to be used
for all fonts.
* Keep spaces in font names
* Drop support for Fontconfig < 2.10.92 to ensure correct font matching
* Bug fixes
* Fix building the assembler code for the x32 ABI (GH #200)
* Fix static linking against libass through pkg-config when libass
uses libiconv
* Fix some small memory leaks, potential crashes and bad data uses
libass (0.13.0)
* Add native font selection backends for OSX (CoreText) and Windows
(DirectWrite). You can now run libass without fontconfig on these
platforms. This fixes problems with fontconfig behaving badly on
these platforms (it could take minutes to scan all system fonts).
Even on Linux, this speeds up loading of embedded fonts (such as
provided by ass_add_font()).
The DirectWrite backend only works on Windows Vista and later. On
XP, fontconfig is still needed. libass can be compiled with both
DirectWrite and fontconfig, and then it will fallback to fontconfig
automatically if DirectWrite is not available at runtime.
* Add ass_get_available_font_providers() API function.
* Change the 4th parameter of ass_set_fonts(). This now selects the
font provider. This is somewhat backwards compatible with the old
behavior, but if you ever passed values other than 0 or 1, your
application might break with this libass release.
* The ass_fonts_update() function now does nothing. It's kept for
backward compatibility only.
* Much faster gaussian blur. This can bring a large speedup with big
blurred signs and such.
* Drop ENCA support
* Bug fixes
* Fix compilation of the freetype rasterizer (disabled by default)
* Fix rendering with some cases of consecutive line breaks (\N\N).
* Fix some memory allocation failure checks
* Avoid system locale dependent behavior by reinventing some standard
C functions
* Fix rendering errors with strikes (GH #193)
* MSVC compilation fixes
libass (0.12.3)
* VSFilter blur compatibility changes
* VSFilter color/alpha parsing compatibility changes
* Bugfixes
* Fix some potential memory leaks and crashes
* Fix large timestamps (larger than about 600 hours)
* Fix a potential crash with the new rasterizer and complex fonts
* Do not apply user-configured line position to non-dialog events
* Strictly clip non-dialog events to the video rectangle
libass (0.12.2)
* Add extern "C" guards to the public headers for C++ compatibility
* Improvements to style override API and implementation
* Bugfixes
* Fix some rasterizer bugs of unknown severity
* Fix a broken memset() of unknown severity
* Make timestamp parsing more lenient, which makes libass accept invalid
files accepted by most other ASS parsers
* Increase compatibility with broken ASS drawings accepted by VSFilter
libass (0.12.1)
* Make ASS drawings with an extremely high number of control points work
This change increases compatibility with VSFilter.
* Bugfixes
* Fix a crash when using newer harfbuzz versions (GH #155)
* Load embedded memory fonts correctly
* Large shadow offsets rendered incorrectly (GH #142)
* Fix incorrect closing of last drawing contour
* Fix issues with undefined behavior in shifts
* Lots of of malloc() error checking
libass (0.12.0)
* Implement fast quad-tree rasterizer in C and SSE2/AVX2 assembly
* Make \be animatable
* Add BorderStyle=4 (GH #105)
* Add selective style override functionality
* ass_set_selective_style_override()
* ass_set_selective_style_override_enabled()
* Support for assembly on DragonFly BSD
* Bugfixes
* Various parser fixes
* Fix premature parser exit on }{ in drawing mode (GH #75)
* Reset clipping mode on every rectangle clip
* Improved error handling for FriBidi and FreeType (GH #131)
* Fix bitmap combining cache
libass (0.11.2)
* Improve yasm version check
* Fix \fscx0 crashes
* Actually export ass_library_version
* Fix change detection with vector clips
* SIMD assembly bugfixes
* Parse drawing numbers as floats (GH #63)
* Fix shaper sign bug (GH #68)
* Initialize \pbo correctly (GH #74)
libass (0.11.1)
* Fix distfile generation
* No functional changes
libass (0.11.0)
* Add bitmap run combining/compositing
This changes the text rendering model and increases compatibility with
VSFilter. It may make rendering faster or slower in certain cases.
* Add AVX/SSE2 implementations for \be and compositing
This adds yasm as (optional) build dependency.
* Add ass_library_version() API funcion (GC #113)
* Add 'profile' program for benchmarks
* Improve gaussian blur
* Fix compilation with MSVC 2013
* Speed up HarfBuzz shaper
* Speed up cache
* Notable compatibility bugfixes
* Reset origin on \fay changes (GC #88)
* Tons of parser fixes
* Fix ascender/descender metrics (GH #8)
* Fix \pbo sign and scaling
* Don't reset wrap style on \r (GC #116, GH #45)
* Notable bugfixes:
* Correctly handle @font advance (GC #94)
* Disable ligatures for non-zero \fsp
* Make sure to release vector clip (GH #22)
libass (0.10.2)
* API additions and some ABI changes, bumped SONAME
* Expose header field for VSFilter color mangling (GC #87)
* Add functions for explicit scaling hints (GC #6)
* Add 'type' field to ASS_Image (GC #31)
* Workaround Freetype's font size grid-fitting where appropriate
* Apply blur to both glyph and border when using opaque box
* Parser bugfixes
* Parse angles in style as number
* Fix fallback event formats
* Make closing ')' optional for some tags
* Fix crazy VSFilter behaviour for \move (GC #90)
* Make \r fall back to line style (GC #104)
* Parse style name like VSFilter
* Ignore junk in nested \t tags
* Make \clip tag arguments mandatory
* Properly handle script and language in the HarfBuzz shaper
* Allow ASS_Track and ASS_Renderer to have different ASS_Library
* Fix stroking in some rare cases
* Correctly handle @font advance (GC #94)
* Fix ascent/descent for some fonts (GC #106)
* Fix ass_step_sub to not require sorted events
* Fix blur scaling
* Don't mutate input strings (GC #88)
* Remove/change some error messages
* Fix various small memory leaks
libass (0.10.1)
* Fix letter spacing
* Add \rSTYLENAME syntax support (GC #43)
* Fix border generation and border style reset (GC #56)
* Fix various issues with bug-for-bug compatibility of
transformations (\fax, \fay, etc.) to VSFilter (GC #46, GC #42)
* Fix drawing parsing (GC #47)
* Various fixes to shaper (GC #54, GC #55, GC#59)
* Fix change detection
* Add ass_set_line_position API to set a vertical subtitle offset
* Fix scaling of drawing baseline offset (\pbo) values
* Fix skipping of zero-width characters for FriBiDi shaper
* Use LTR text base direction by default, similar to VSFilter
libass (0.10.0)
* Bidirectional layout and Arabic shaping via FriBidi (GC #13)
* OpenType shaping via HarfBuzz-ng (GC #13)
* Add API for shaper configuration
* Add support for `Language' Script Info property, this can be used for
hinting the text language
* Vertical layout improvements
* Use `vert' and `vkna' OpenType features for vertical glyph variants
* Position rotated glyphs onto baseline
* Parse font encoding property for base text direction hinting
* Refactor cache system
* Use generic outlines in place of FreeType glyphs
* Direct outline bitmap rendering
* Fix whitespace trimming (GC #35)
* Do not render border if there's no shadow or glyph (GC #29)
* Adjust spacing after a italic to non-italic style change (GC #37)
* Fix fade timing
* Fix x positioning with borders (GC #27)
* Build system tweaks
libass (0.9.12)
* Switch to permissive (ISC) license
* Support \fs+ and \fs- syntax for modifying font size
* Fix word-wrapping
* Improved charmap fallback matching
* Handle a few more VSFilter quirks correctly
* Add a sensible default style
* Fix compilation against libpng 1.5
libass (0.9.11)
* Fix serious memory leaks
* Reduce frame/drawing initialization overhead
libass (0.9.10)
* Basic (incorrect, but working) support for @font vertical text layout
* Fix multiple faces per font attachment
* charmap selection fixes
* Add ass_flush_events API function
* Improve fullname font matching
* Better PAR correction if text transforms are used
* Calculate drawing bounding box like VSFilter
* Performance improvements
* Cache vector clip masks
* Avoid unnecessary glyph copies
* Various rendering fixes
libass (0.9.9)
* Parse numbers in a locale-independent way
* Remove support for freetype < 2.2.1, fontconfig < 2.4.1; this especially
means libass will not extract fonts into the file system anymore
* Disable script file size limit
* Match fonts against the full name ("name for humans")
* Reset clip mode after \iclip
* Improve VSFilter compatibility
* Update API documentation
* A couple of smaller fixes and cleanups
libass (0.9.8)
* Support \q override tag
* Support wrap style 1 (i.e. wrap, but do not equalize line lengths)
* Support border style 3 (opaque box)
* Use the event bounding box (instead of vertical position and height) for
collision detection
* Embold glyphs if no bold variant is available, but was requested
* Modify \fax to be similar to VSFilter
* Trim spaces after line wrapping
* Fix border/shadow overlap combining in some cases
* Disable kerning by default. Use "Kerning=yes" style override or
"Kerning: yes" in [Script Info] to enable it
* Slight bitmap handling optimizations
* Various bugfixes
libass (0.9.7)
* Build system fixes
* Fixed cache lookup and overload problems
* All globals have been eliminated, libass is reentrant
* Dynamically allocate glyph and line buffers
* Fix up stroking of big borders
* Support empty lines (\N\N)
* Support for the following override tags:
\fax, \fay, \xshad, \yshad, \ybord, \xbord, \iclip, \u, \s, \p, \pbo
* Full subpixel accuracy for positioning
* PAR and rotation correction for EOSD rendering
* Drawing mode (including vector \clip and \iclip)
* Fixed a few memory leaks
* Removed MPlayer compatibility code
* Introduced message handling callback
* Various fixes to match VSFilter quirks and Windows font metrics
* Lots of bugfixes
LibASS (0.9.6)
* Various fixes and updates to match VSFilter renderer.
* Support \blur tag and ScaledBordersAndShadow property.
* Fractional arguments and subpixel accuracy.
* Keep positions when pan-and-scan is used.
* Lots of bugfixes and other changes.
LibASS (0.9.5)
* Support '=' and '.' in style name in arguments to ass_set_style_overrides().
* Allow overriding [Script Info] parameters with ass_set_style_overrides().
* Add workarounds for some buggy fonts.
* Remove buggy workarounds for some other fonts.
* Fixed ass_set_line_spacing() (was broken before).
* Negative margin sizes are now used for image cropping.
* Better handling of behind-the-camera objects.
* Case insensitive parsing of SSA/ASS section headers.
* Improved font matching.
* When 2 styles have the same name, the later one is used.
* Fixed several other bugs.

17
Makefile.am Normal file
View File

@ -0,0 +1,17 @@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign
EXTRA_DIST = libass.pc.in Changelog
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libass.pc
if HAVE_LIBPNG
test = test
endif
if ENABLE_PROFILE
profile = profile
endif
SUBDIRS = libass $(test) $(profile)

895
Makefile.in Normal file
View File

@ -0,0 +1,895 @@
# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
$(am__configure_deps) $(am__DIST_COMMON)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES = libass.pc
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
ctags-recursive dvi-recursive html-recursive info-recursive \
install-data-recursive install-dvi-recursive \
install-exec-recursive install-html-recursive \
install-info-recursive install-pdf-recursive \
install-ps-recursive install-recursive installcheck-recursive \
installdirs-recursive pdf-recursive ps-recursive \
tags-recursive uninstall-recursive
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
am__installdirs = "$(DESTDIR)$(pkgconfigdir)"
DATA = $(pkgconfig_DATA)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
$(RECURSIVE_TARGETS) \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
cscope distdir dist dist-all distcheck
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
$(LISP)config.h.in
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
CSCOPE = cscope
DIST_SUBDIRS = libass test profile
am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
$(srcdir)/libass.pc.in COPYING compile config.guess config.sub \
install-sh ltmain.sh missing
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
am__remove_distdir = \
if test -d "$(distdir)"; then \
find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
&& rm -rf "$(distdir)" \
|| { sleep 5 && rm -rf "$(distdir)"; }; \
else :; fi
am__post_remove_distdir = $(am__remove_distdir)
am__relativize = \
dir0=`pwd`; \
sed_first='s,^\([^/]*\)/.*$$,\1,'; \
sed_rest='s,^[^/]*/*,,'; \
sed_last='s,^.*/\([^/]*\)$$,\1,'; \
sed_butlast='s,/*[^/]*$$,,'; \
while test -n "$$dir1"; do \
first=`echo "$$dir1" | sed -e "$$sed_first"`; \
if test "$$first" != "."; then \
if test "$$first" = ".."; then \
dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
else \
first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
if test "$$first2" = "$$first"; then \
dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
else \
dir2="../$$dir2"; \
fi; \
dir0="$$dir0"/"$$first"; \
fi; \
fi; \
dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
done; \
reldir="$$dir2"
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = --best
DIST_TARGETS = dist-gzip
distuninstallcheck_listfiles = find . -type f -print
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
distcleancheck_listfiles = find . -type f -print
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AS = @AS@
ASFLAGS = @ASFLAGS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCAS = @CCAS@
CCASDEPMODE = @CCASDEPMODE@
CCASFLAGS = @CCASFLAGS@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
FREETYPE_LIBS = @FREETYPE_LIBS@
FRIBIDI_CFLAGS = @FRIBIDI_CFLAGS@
FRIBIDI_LIBS = @FRIBIDI_LIBS@
GREP = @GREP@
HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
LIBPNG_LIBS = @LIBPNG_LIBS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
PKG_LIBS_DEFAULT = @PKG_LIBS_DEFAULT@
PKG_LIBS_PRIVATE = @PKG_LIBS_PRIVATE@
PKG_REQUIRES_DEFAULT = @PKG_REQUIRES_DEFAULT@
PKG_REQUIRES_PRIVATE = @PKG_REQUIRES_PRIVATE@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
nasm_check = @nasm_check@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign
EXTRA_DIST = libass.pc.in Changelog
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libass.pc
@HAVE_LIBPNG_TRUE@test = test
@ENABLE_PROFILE_TRUE@profile = profile
SUBDIRS = libass $(test) $(profile)
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive
.SUFFIXES:
am--refresh: Makefile
@:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
$(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
&& exit 0; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
echo ' $(SHELL) ./config.status'; \
$(SHELL) ./config.status;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(SHELL) ./config.status --recheck
$(top_srcdir)/configure: $(am__configure_deps)
$(am__cd) $(srcdir) && $(AUTOCONF)
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
$(am__aclocal_m4_deps):
config.h: stamp-h1
@test -f $@ || rm -f stamp-h1
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
@rm -f stamp-h1
cd $(top_builddir) && $(SHELL) ./config.status config.h
$(srcdir)/config.h.in: $(am__configure_deps)
($(am__cd) $(top_srcdir) && $(AUTOHEADER))
rm -f stamp-h1
touch $@
distclean-hdr:
-rm -f config.h stamp-h1
libass.pc: $(top_builddir)/config.status $(srcdir)/libass.pc.in
cd $(top_builddir) && $(SHELL) ./config.status $@
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
distclean-libtool:
-rm -f libtool config.lt
install-pkgconfigDATA: $(pkgconfig_DATA)
@$(NORMAL_INSTALL)
@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
done
uninstall-pkgconfigDATA:
@$(NORMAL_UNINSTALL)
@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
# This directory's subdirectories are mostly independent; you can cd
# into them and run 'make' without going through this Makefile.
# To change the values of 'make' variables: instead of editing Makefiles,
# (1) if the variable is set in 'config.status', edit 'config.status'
# (which will cause the Makefiles to be regenerated when you run 'make');
# (2) otherwise, pass the desired values on the 'make' command line.
$(am__recursive_targets):
@fail=; \
if $(am__make_keepgoing); then \
failcom='fail=yes'; \
else \
failcom='exit 1'; \
fi; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
case "$@" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
*) list='$(SUBDIRS)' ;; \
esac; \
for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-recursive
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test ! -f $$subdir/TAGS || \
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-recursive
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscope: cscope.files
test ! -s cscope.files \
|| $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
clean-cscope:
-rm -f cscope.files
cscope.files: clean-cscope cscopelist
cscopelist: cscopelist-recursive
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
distdir: $(DISTFILES)
$(am__remove_distdir)
test -d "$(distdir)" || mkdir "$(distdir)"
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
$(am__make_dryrun) \
|| test -d "$(distdir)/$$subdir" \
|| $(MKDIR_P) "$(distdir)/$$subdir" \
|| exit 1; \
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
$(am__relativize); \
new_distdir=$$reldir; \
dir1=$$subdir; dir2="$(top_distdir)"; \
$(am__relativize); \
new_top_distdir=$$reldir; \
echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
($(am__cd) $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$$new_top_distdir" \
distdir="$$new_distdir" \
am__remove_distdir=: \
am__skip_length_check=: \
am__skip_mode_fix=: \
distdir) \
|| exit 1; \
fi; \
done
-test -n "$(am__skip_mode_fix)" \
|| find "$(distdir)" -type d ! -perm -755 \
-exec chmod u+rwx,go+rx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r "$(distdir)"
dist-gzip: distdir
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
$(am__post_remove_distdir)
dist-bzip2: distdir
tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
$(am__post_remove_distdir)
dist-lzip: distdir
tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
$(am__post_remove_distdir)
dist-xz: distdir
tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
$(am__post_remove_distdir)
dist-tarZ: distdir
@echo WARNING: "Support for distribution archives compressed with" \
"legacy program 'compress' is deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
$(am__post_remove_distdir)
dist-shar: distdir
@echo WARNING: "Support for shar distribution archives is" \
"deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
$(am__post_remove_distdir)
dist-zip: distdir
-rm -f $(distdir).zip
zip -rq $(distdir).zip $(distdir)
$(am__post_remove_distdir)
dist dist-all:
$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
$(am__post_remove_distdir)
# This target untars the dist file and tries a VPATH configuration. Then
# it guarantees that the distribution is self-contained by making another
# tarfile.
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
*.tar.lz*) \
lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
*.tar.xz*) \
xz -dc $(distdir).tar.xz | $(am__untar) ;;\
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
esac
chmod -R a-w $(distdir)
chmod u+w $(distdir)
mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
chmod a-w $(distdir)
test -d $(distdir)/_build || exit 0; \
dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
&& dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
&& am__cwd=`pwd` \
&& $(am__cd) $(distdir)/_build/sub \
&& ../../configure \
$(AM_DISTCHECK_CONFIGURE_FLAGS) \
$(DISTCHECK_CONFIGURE_FLAGS) \
--srcdir=../.. --prefix="$$dc_install_base" \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
&& $(MAKE) $(AM_MAKEFLAGS) check \
&& $(MAKE) $(AM_MAKEFLAGS) install \
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
&& $(MAKE) $(AM_MAKEFLAGS) uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
distuninstallcheck \
&& chmod -R a-w "$$dc_install_base" \
&& ({ \
(cd ../.. && umask 077 && mkdir "$$dc_destdir") \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
} || { rm -rf "$$dc_destdir"; exit 1; }) \
&& rm -rf "$$dc_destdir" \
&& $(MAKE) $(AM_MAKEFLAGS) dist \
&& rm -rf $(DIST_ARCHIVES) \
&& $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
&& cd "$$am__cwd" \
|| exit 1
$(am__post_remove_distdir)
@(echo "$(distdir) archives ready for distribution: "; \
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
distuninstallcheck:
@test -n '$(distuninstallcheck_dir)' || { \
echo 'ERROR: trying to run $@ with an empty' \
'$$(distuninstallcheck_dir)' >&2; \
exit 1; \
}; \
$(am__cd) '$(distuninstallcheck_dir)' || { \
echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
exit 1; \
}; \
test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left after uninstall:" ; \
if test -n "$(DESTDIR)"; then \
echo " (check DESTDIR support)"; \
fi ; \
$(distuninstallcheck_listfiles) ; \
exit 1; } >&2
distcleancheck: distclean
@if test '$(srcdir)' = . ; then \
echo "ERROR: distcleancheck can only run from a VPATH build" ; \
exit 1 ; \
fi
@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left in build directory after distclean:" ; \
$(distcleancheck_listfiles) ; \
exit 1; } >&2
check-am: all-am
check: check-recursive
all-am: Makefile $(DATA) config.h
installdirs: installdirs-recursive
installdirs-am:
for dir in "$(DESTDIR)$(pkgconfigdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-recursive
install-exec: install-exec-recursive
install-data: install-data-recursive
uninstall: uninstall-recursive
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-recursive
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-recursive
clean-am: clean-generic clean-libtool mostlyclean-am
distclean: distclean-recursive
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -f Makefile
distclean-am: clean-am distclean-generic distclean-hdr \
distclean-libtool distclean-tags
dvi: dvi-recursive
dvi-am:
html: html-recursive
html-am:
info: info-recursive
info-am:
install-data-am: install-pkgconfigDATA
install-dvi: install-dvi-recursive
install-dvi-am:
install-exec-am:
install-html: install-html-recursive
install-html-am:
install-info: install-info-recursive
install-info-am:
install-man:
install-pdf: install-pdf-recursive
install-pdf-am:
install-ps: install-ps-recursive
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf $(top_srcdir)/autom4te.cache
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-recursive
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
pdf: pdf-recursive
pdf-am:
ps: ps-recursive
ps-am:
uninstall-am: uninstall-pkgconfigDATA
.MAKE: $(am__recursive_targets) all install-am install-strip
.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
am--refresh check check-am clean clean-cscope clean-generic \
clean-libtool cscope cscopelist-am ctags ctags-am dist \
dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
dist-xz dist-zip distcheck distclean distclean-generic \
distclean-hdr distclean-libtool distclean-tags distcleancheck \
distdir distuninstallcheck dvi dvi-am html html-am info \
info-am install install-am install-data install-data-am \
install-dvi install-dvi-am install-exec install-exec-am \
install-html install-html-am install-info install-info-am \
install-man install-pdf install-pdf-am install-pkgconfigDATA \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs installdirs-am maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic \
mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-pkgconfigDATA
.PRECIOUS: Makefile
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

1490
aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

347
compile Executable file
View File

@ -0,0 +1,347 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2012-10-14.11; # UTC
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

1421
config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

109
config.h.in Normal file
View File

@ -0,0 +1,109 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* ASM enabled */
#undef CONFIG_ASM
/* found CoreText framework */
#undef CONFIG_CORETEXT
/* found DirectWrite */
#undef CONFIG_DIRECTWRITE
/* found fontconfig via pkg-config */
#undef CONFIG_FONTCONFIG
/* found freetype2 via pkg-config */
#undef CONFIG_FREETYPE
/* found fribidi via pkg-config */
#undef CONFIG_FRIBIDI
/* found harfbuzz-ng via pkg-config */
#undef CONFIG_HARFBUZZ
/* use iconv */
#undef CONFIG_ICONV
/* use small tiles */
#undef CONFIG_LARGE_TILES
/* found libpng via pkg-config */
#undef CONFIG_LIBPNG
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <iconv.h> header file. */
#undef HAVE_ICONV_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if stdbool.h conforms to C99. */
#undef HAVE_STDBOOL_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strdup' function. */
#undef HAVE_STRDUP
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `strndup' function. */
#undef HAVE_STRNDUP
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION

1807
config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

16876
configure vendored Executable file

File diff suppressed because it is too large Load Diff

269
configure.ac Normal file
View File

@ -0,0 +1,269 @@
AC_INIT(libass, 0.14.0)
AM_INIT_AUTOMAKE
AC_CONFIG_MACRO_DIR([m4])
# Disable Fortran checks
define([AC_LIBTOOL_LANG_F77_CONFIG], [:])
LT_INIT
AC_CONFIG_SRCDIR([libass/ass.c])
AC_CONFIG_HEADER([config.h])
# Checks for programs.
AC_PROG_CC
AC_PROG_CPP
AM_PROG_CC_C_O
AM_PROG_AS
# Checks for header files.
AC_HEADER_STDC
AC_HEADER_STDBOOL
AC_CHECK_HEADERS([stdint.h iconv.h])
# Checks for library functions.
AC_CHECK_FUNCS([strdup strndup])
# Checks for libraries.
# Add them to pkg-config for static linking.
OLDLIBS="$LIBS"
LIBS=
use_libiconv=false
AC_SEARCH_LIBS([libiconv_open], [iconv], use_libiconv=true)
AC_SEARCH_LIBS([iconv_open], [iconv], use_libiconv=true)
AC_CHECK_LIB([m], [fabs])
pkg_libs="$LIBS"
LIBS="$OLDLIBS $LIBS"
# Check for libraries via pkg-config
AC_ARG_ENABLE([test], AS_HELP_STRING([--enable-test],
[enable test program (requires libpng) @<:@default=no@:>@]))
AC_ARG_ENABLE([profile], AS_HELP_STRING([--enable-profile],
[enable profiling program @<:@default=no@:>@]))
AC_ARG_ENABLE([fontconfig], AS_HELP_STRING([--disable-fontconfig],
[disable fontconfig support @<:@default=enabled@:>@]))
AC_ARG_ENABLE([directwrite], AS_HELP_STRING([--disable-directwrite],
[disable DirectWrite support (win32 only) @<:@default=check@:>@]))
AC_ARG_ENABLE([coretext], AS_HELP_STRING([--disable-coretext],
[disable CoreText support (OSX only) @<:@default=check@:>@]))
AC_ARG_ENABLE([require-system-font-provider], AS_HELP_STRING([--disable-require-system-font-provider],
[allow compilation even if no system font provider was found @<:@default=enabled:>@]))
AC_ARG_ENABLE([harfbuzz], AS_HELP_STRING([--disable-harfbuzz],
[disable HarfBuzz support @<:@default=check@:>@]))
AC_ARG_ENABLE([asm], AS_HELP_STRING([--disable-asm],
[disable compiling with ASM @<:@default=check@:>@]))
AC_ARG_ENABLE([large-tiles], AS_HELP_STRING([--enable-large-tiles],
[use larger tiles in the rasterizer (better performance, slightly worse quality) @<:@default=disabled@:>@]))
AS_IF([test x$enable_asm != xno], [
AS_CASE([$host],
[i?86-*], [
INTEL=true
AS=nasm
X86=true
BITS=32
BITTYPE=32
ASFLAGS="$ASFLAGS -DARCH_X86_64=0" ],
[x86_64-*-gnux32|amd64-*-gnux32], [
AS=nasm
INTEL=true
X64=true
BITS=64
BITTYPE=x32
ASFLAGS="$ASFLAGS -DARCH_X86_64=1 -DPIC" ],
[x86_64-*|amd64-*], [
AS=nasm
INTEL=true
X64=true
BITS=64
BITTYPE=64
ASFLAGS="$ASFLAGS -DARCH_X86_64=1 -DPIC" ],
)
AS_IF([test x$INTEL = xtrue], [
AC_CHECK_PROG([nasm_check], [$AS], [yes])
AS_IF([test x$nasm_check != xyes], [
AC_MSG_WARN(nasm was not found; ASM functions are disabled.)
AC_MSG_WARN(Install nasm for a significantly faster libass build.)
enable_asm=no
], [
AS_CASE([$host],
[*darwin*], [
ASFLAGS="$ASFLAGS -f macho$BITTYPE -DPREFIX -DHAVE_ALIGNED_STACK=1" ],
[*linux*|*dragonfly*|*bsd*|*solaris*], [
ASFLAGS="$ASFLAGS -f elf$BITTYPE -DHAVE_ALIGNED_STACK=1" ],
[*cygwin*|*mingw*], [
ASFLAGS="$ASFLAGS -f win$BITTYPE"
AS_IF([test x$BITS = x64], [
ASFLAGS="$ASFLAGS -DHAVE_ALIGNED_STACK=1"
], [
ASFLAGS="$ASFLAGS -DHAVE_ALIGNED_STACK=0 -DPREFIX"
])
])
ASFLAGS="$ASFLAGS -DHAVE_CPUNOP=0 -Dprivate_prefix=ass"
AC_MSG_CHECKING([if $AS supports vpmovzxwd])
echo "vpmovzxwd ymm0, xmm0" > conftest.asm
AS_IF([$AS conftest.asm $ASFLAGS -o conftest.o >conftest.log 2>&1], [
AC_MSG_RESULT([yes])
], [
AC_MSG_RESULT([no])
VER=`($AS --version || echo no assembler) 2>/dev/null | head -n 1`
AC_MSG_WARN([nasm is too old (found $VER); ASM functions are disabled.])
AC_MSG_WARN([Install nasm-2.10 or later for a significantly faster libass build.])
enable_asm=no
])
rm conftest.asm conftest.o > /dev/null 2>&1
])
])
])
AC_SUBST([ASFLAGS], ["$ASFLAGS"])
AC_SUBST([AS], ["$AS"])
AM_CONDITIONAL([ASM], [test x$enable_asm != xno])
AM_CONDITIONAL([INTEL], [test x$INTEL = xtrue])
AM_CONDITIONAL([X86], [test x$X86 = xtrue])
AM_CONDITIONAL([X64], [test x$X64 = xtrue])
AM_COND_IF([ASM],
[AC_DEFINE(CONFIG_ASM, 1, [ASM enabled])],
[AC_DEFINE(CONFIG_ASM, 0, [ASM enabled])]
)
AM_CONDITIONAL([ENABLE_LARGE_TILES], [test x$enable_large_tiles = xyes])
AM_COND_IF([ENABLE_LARGE_TILES],
[AC_DEFINE(CONFIG_LARGE_TILES, 1, [use large tiles])]
[AC_DEFINE(CONFIG_LARGE_TILES, 0, [use small tiles])],
)
PKG_CHECK_MODULES([FREETYPE], freetype2 >= 9.10.3, [
CFLAGS="$CFLAGS $FREETYPE_CFLAGS"
CXXFLAGS="$CFLAGS $FREETYPE_CFLAGS"
LIBS="$LIBS $FREETYPE_LIBS"
AC_DEFINE(CONFIG_FREETYPE, 1, [found freetype2 via pkg-config])
])
PKG_CHECK_MODULES([FRIBIDI], fribidi >= 0.19.0, [
CFLAGS="$CFLAGS $FRIBIDI_CFLAGS"
LIBS="$LIBS $FRIBIDI_LIBS"
AC_DEFINE(CONFIG_FRIBIDI, 1, [found fribidi via pkg-config])
])
if test x$enable_fontconfig != xno; then
PKG_CHECK_MODULES([FONTCONFIG], fontconfig >= 2.10.92, [
CFLAGS="$CFLAGS $FONTCONFIG_CFLAGS"
LIBS="$LIBS $FONTCONFIG_LIBS"
AC_DEFINE(CONFIG_FONTCONFIG, 1, [found fontconfig via pkg-config])
fontconfig=true
], [fontconfig=false])
fi
AM_CONDITIONAL([FONTCONFIG], [test x$fontconfig = xtrue])
if test x$enable_coretext != xno; then
# Linking to CoreText directly only works from Mountain Lion and iOS.
# In earlier OS X releases CoreText was part of the ApplicationServices
# umbrella framework.
AC_MSG_CHECKING([for CORETEXT])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM(
[[#include <ApplicationServices/ApplicationServices.h>]],
[[CTFontDescriptorCopyAttribute(NULL, kCTFontURLAttribute);]])
], [
LIBS="$LIBS -framework ApplicationServices -framework CoreFoundation"
AC_DEFINE(CONFIG_CORETEXT, 1, [found CoreText in ApplicationServices framework])
coretext=true
AC_MSG_RESULT([yes])
], [
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM(
[[#include <CoreText/CoreText.h>]],
[[CTFontDescriptorCopyAttribute(NULL, kCTFontURLAttribute);]])
], [
LIBS="$LIBS -framework CoreText -framework CoreFoundation"
AC_DEFINE(CONFIG_CORETEXT, 1, [found CoreText framework])
coretext=true
AC_MSG_RESULT([yes])
], [
coretext=false
AC_MSG_RESULT([no])
])
])
fi
AM_CONDITIONAL([CORETEXT], [test x$coretext = xtrue])
if test x$enable_directwrite != xno; then
# Linking to DirectWrite directly only works from Windows
AC_MSG_CHECKING([for DIRECTWRITE])
AC_LINK_IFELSE([
AC_LANG_PROGRAM(
[[#include <windows.h>]],
[[;]],)
], [
AC_DEFINE(CONFIG_DIRECTWRITE, 1, [found DirectWrite])
directwrite=true
AC_MSG_RESULT([yes])
], [
directwrite=false
AC_MSG_RESULT([no])
])
fi
AM_CONDITIONAL([DIRECTWRITE], [test x$directwrite = xtrue])
if test x$enable_harfbuzz != xno; then
PKG_CHECK_MODULES([HARFBUZZ], harfbuzz >= 0.9.5, [
CFLAGS="$CFLAGS $HARFBUZZ_CFLAGS"
LIBS="$LIBS $HARFBUZZ_LIBS"
AC_DEFINE(CONFIG_HARFBUZZ, 1, [found harfbuzz-ng via pkg-config])
harfbuzz=true
], [harfbuzz=false])
fi
libpng=false
if test x$enable_test = xyes; then
PKG_CHECK_MODULES([LIBPNG], libpng >= 1.2.0, [
CFLAGS="$CFLAGS $LIBPNG_CFLAGS"
AC_DEFINE(CONFIG_LIBPNG, 1, [found libpng via pkg-config])
libpng=true])
fi
AM_CONDITIONAL([HAVE_LIBPNG], [test x$libpng = xtrue])
AM_CONDITIONAL([ENABLE_PROFILE], [test x$enable_profile = xyes])
# add packages to pkg-config for static linking
if test "$use_libiconv" = true; then
AC_DEFINE(CONFIG_ICONV, 1, [use iconv])
if test x"$ac_cv_search_libiconv_open" != x"none required" &&
test x"$ac_cv_search_iconv_open" != x"none required"; then
pkg_libs="${pkg_libs} -liconv"
fi
fi
pkg_requires="freetype2 >= 9.10.3"
pkg_requires="fribidi >= 0.19.0, ${pkg_requires}"
if test x$fontconfig = xtrue; then
pkg_requires="fontconfig >= 2.10.92, ${pkg_requires}"
fi
if test x$harfbuzz = xtrue; then
pkg_requires="harfbuzz >= 0.9.5, ${pkg_requires}"
fi
if test x$enable_require_system_font_provider != xno &&
test x$fontconfig != xtrue &&
test x$directwrite != xtrue &&
test x$coretext != xtrue
then
AC_MSG_ERROR([\
Either DirectWrite (on Windows), CoreText (on OSX), or Fontconfig \
(Linux, other) is required. If you really want to compile without \
a system font provider, add --disable-require-system-font-provider])
fi
AC_SUBST([PKG_LIBS_DEFAULT], [$(test x$enable_shared = xno && echo ${pkg_libs})])
AC_SUBST([PKG_REQUIRES_DEFAULT], [$(test x$enable_shared = xno && echo ${pkg_requires})])
AC_SUBST([PKG_LIBS_PRIVATE], [$(test x$enable_shared = xno || echo ${pkg_libs})])
AC_SUBST([PKG_REQUIRES_PRIVATE], [$(test x$enable_shared = xno || echo ${pkg_requires})])
# Setup output beautifier.
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_FILES([Makefile libass/Makefile test/Makefile profile/Makefile libass.pc])
AC_OUTPUT

791
depcomp Executable file
View File

@ -0,0 +1,791 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2013-05-30.07; # UTC
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by 'PROGRAMS ARGS'.
object Object file output by 'PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
# Get the directory component of the given path, and save it in the
# global variables '$dir'. Note that this directory component will
# be either empty or ending with a '/' character. This is deliberate.
set_dir_from ()
{
case $1 in
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
*) dir=;;
esac
}
# Get the suffix-stripped basename of the given path, and save it the
# global variable '$base'.
set_base_from ()
{
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
}
# If no dependency file was actually created by the compiler invocation,
# we still have to create a dummy depfile, to avoid errors with the
# Makefile "include basename.Plo" scheme.
make_dummy_depfile ()
{
echo "#dummy" > "$depfile"
}
# Factor out some common post-processing of the generated depfile.
# Requires the auxiliary global variable '$tmpdepfile' to be set.
aix_post_process_depfile ()
{
# If the compiler actually managed to produce a dependency file,
# post-process it.
if test -f "$tmpdepfile"; then
# Each line is of the form 'foo.o: dependency.h'.
# Do two passes, one to just change these to
# $object: dependency.h
# and one to simply output
# dependency.h:
# which is needed to avoid the deleted-header problem.
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
} > "$depfile"
rm -f "$tmpdepfile"
else
make_dummy_depfile
fi
}
# A tabulation character.
tab=' '
# A newline character.
nl='
'
# Character ranges might be problematic outside the C locale.
# These definitions help.
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digits=0123456789
alpha=${upper}${lower}
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Avoid interferences from the environment.
gccflag= dashmflag=
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
cygpath_u="cygpath -u -f -"
if test "$depmode" = msvcmsys; then
# This is just like msvisualcpp but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvisualcpp
fi
if test "$depmode" = msvc7msys; then
# This is just like msvc7 but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvc7
fi
if test "$depmode" = xlc; then
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say). Also, it might not be
## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The second -e expression handles DOS-style file names with drive
# letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the "deleted header file" problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
## Some versions of gcc put a space before the ':'. On the theory
## that the space means something, we add a space to the output as
## well. hp depmode also adds that space, but also prefixes the VPATH
## to the object. Take care to not repeat it in the output.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
| tr "$nl" ' ' >> "$depfile"
echo >> "$depfile"
# The second pass generates a dummy entry for each header file.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile"
;;
xlc)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts '$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
tmpdepfile3=$dir.libs/$base.u
"$@" -Wc,-M
else
tmpdepfile1=$dir$base.u
tmpdepfile2=$dir$base.u
tmpdepfile3=$dir$base.u
"$@" -M
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
aix_post_process_depfile
;;
tcc)
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
# FIXME: That version still under development at the moment of writing.
# Make that this statement remains true also for stable, released
# versions.
# It will wrap lines (doesn't matter whether long or short) with a
# trailing '\', as in:
#
# foo.o : \
# foo.c \
# foo.h \
#
# It will put a trailing '\' even on the last line, and will use leading
# spaces rather than leading tabs (at least since its commit 0394caf7
# "Emit spaces for -MD").
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
# We have to change lines of the first kind to '$object: \'.
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
# And for each line of the second kind, we have to emit a 'dep.h:'
# dummy dependency, to avoid the deleted-header problem.
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
## The order of this option in the case statement is important, since the
## shell code in configure will try each of these formats in the order
## listed in this file. A plain '-MD' option would be understood by many
## compilers, so we must ensure this comes after the gcc and icc options.
pgcc)
# Portland's C compiler understands '-MD'.
# Will always output deps to 'file.d' where file is the root name of the
# source file under compilation, even if file resides in a subdirectory.
# The object file name does not affect the name of the '.d' file.
# pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
set_dir_from "$object"
# Use the source, not the object, to determine the base name, since
# that's sadly what pgcc will do too.
set_base_from "$source"
tmpdepfile=$base.d
# For projects that build the same source file twice into different object
# files, the pgcc approach of using the *source* file root name can cause
# problems in parallel builds. Use a locking strategy to avoid stomping on
# the same $tmpdepfile.
lockdir=$base.d-lock
trap "
echo '$0: caught signal, cleaning up...' >&2
rmdir '$lockdir'
exit 1
" 1 2 13 15
numtries=100
i=$numtries
while test $i -gt 0; do
# mkdir is a portable test-and-set.
if mkdir "$lockdir" 2>/dev/null; then
# This process acquired the lock.
"$@" -MD
stat=$?
# Release the lock.
rmdir "$lockdir"
break
else
# If the lock is being held by a different process, wait
# until the winning process is done or we timeout.
while test -d "$lockdir" && test $i -gt 0; do
sleep 1
i=`expr $i - 1`
done
fi
i=`expr $i - 1`
done
trap - 1 2 13 15
if test $i -le 0; then
echo "$0: failed to acquire lock after $numtries attempts" >&2
echo "$0: check lockdir '$lockdir'" >&2
exit 1
fi
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
# Add 'dependent.h:' lines.
sed -ne '2,${
s/^ *//
s/ \\*$//
s/$/:/
p
}' "$tmpdepfile" >> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in 'foo.d' instead, so we check for that too.
# Subdirectories are respected.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
# Libtool generates 2 separate objects for the 2 libraries. These
# two compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir$base.o.d # libtool 1.5
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
# Same post-processing that is required for AIX mode.
aix_post_process_depfile
;;
msvc7)
if test "$libtool" = yes; then
showIncludes=-Wc,-showIncludes
else
showIncludes=-showIncludes
fi
"$@" $showIncludes > "$tmpdepfile"
stat=$?
grep -v '^Note: including file: ' "$tmpdepfile"
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The first sed program below extracts the file names and escapes
# backslashes for cygpath. The second sed program outputs the file
# name when reading, but also accumulates all include files in the
# hold buffer in order to output them again at the end. This only
# works with sed implementations that can handle large buffers.
sed < "$tmpdepfile" -n '
/^Note: including file: *\(.*\)/ {
s//\1/
s/\\/\\\\/g
p
}' | $cygpath_u | sort -u | sed -n '
s/ /\\ /g
s/\(.*\)/'"$tab"'\1 \\/p
s/.\(.*\) \\/\1:/
H
$ {
s/.*/'"$tab"'/
G
p
}' >> "$depfile"
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
rm -f "$tmpdepfile"
;;
msvc7msys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for ':'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this sed invocation
# correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no eat=no
for arg
do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
if test $eat = yes; then
eat=no
continue
fi
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-arch)
eat=yes ;;
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix=`echo "$object" | sed 's/^.*\././'`
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
# makedepend may prepend the VPATH from the source file name to the object.
# No need to regex-escape $object, excess matching of '.' is harmless.
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process the last invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed '1,2d' "$tmpdepfile" \
| tr ' ' "$nl" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E \
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
| sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
IFS=" "
for arg
do
case "$arg" in
-o)
shift
;;
$object)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E 2>/dev/null |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
echo "$tab" >> "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvcmsys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

501
install-sh Executable file
View File

@ -0,0 +1,501 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2013-12-25.23; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve the last data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-s) stripcmd=$stripprog;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# Create intermediate dirs using mode 755 as modified by the umask.
# This is like FreeBSD 'install' as of 1997-10-28.
umask=`umask`
case $stripcmd.$umask in
# Optimize common cases.
*[2367][2367]) mkdir_umask=$umask;;
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
*[0-7])
mkdir_umask=`expr $umask + 22 \
- $umask % 100 % 40 + $umask % 20 \
- $umask % 10 % 4 + $umask % 2
`;;
*) mkdir_umask=$umask,go-w;;
esac
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
case $umask in
*[123567][0-7][0-7])
# POSIX mkdir -p sets u+wx bits regardless of umask, which
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
if (umask $mkdir_umask &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
ls_ld_tmpdir=`ls -ld "$tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/d" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
fi
trap '' 0;;
esac;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask=$mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=$dstdir/_inst.$$_
rmtmp=$dstdir/_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd -f "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

13
libass.pc.in Normal file
View File

@ -0,0 +1,13 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libass
Description: LibASS is an SSA/ASS subtitles rendering library
Version: @PACKAGE_VERSION@
Requires: @PKG_REQUIRES_DEFAULT@
Requires.private: @PKG_REQUIRES_PRIVATE@
Libs: -L${libdir} -lass @PKG_LIBS_DEFAULT@
Libs.private: @PKG_LIBS_PRIVATE@
Cflags: -I${includedir}

63
libass/Makefile.am Normal file
View File

@ -0,0 +1,63 @@
AM_CFLAGS = -std=gnu99 -Wall -Wextra -Wno-sign-compare -Wno-unused-parameter \
-Werror-implicit-function-declaration -Wstrict-prototypes \
-Wpointer-arith -Wredundant-decls -Wno-missing-field-initializers\
-D_GNU_SOURCE
LIBASS_LT_CURRENT = 9
LIBASS_LT_REVISION = 2
LIBASS_LT_AGE = 0
nasm_verbose = $(nasm_verbose_$(V))
nasm_verbose_ = $(nasm_verbose_$(AM_DEFAULT_VERBOSITY))
nasm_verbose_0 = @echo " NASM " $@;
.asm.lo:
$(nasm_verbose)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(AS) $(ASFLAGS) -I$(srcdir)/ -o $@ $< -prefer-non-pic
SRC_INTEL = x86/rasterizer.asm x86/blend_bitmaps.asm x86/blur.asm x86/cpuid.asm \
x86/cpuid.h
SRC_INTEL64 = x86/be_blur.asm
SRC_FONTCONFIG = ass_fontconfig.c ass_fontconfig.h
SRC_DIRECTWRITE = ass_directwrite.c ass_directwrite.h dwrite_c.h
SRC_CORETEXT = ass_coretext.c ass_coretext.h
lib_LTLIBRARIES = libass.la
libass_la_SOURCES = ass.h ass.c ass_types.h ass_utils.h ass_utils.c \
ass_compat.h ass_string.h ass_string.c ass_strtod.c \
ass_library.h ass_library.c ass_cache.h ass_cache.c ass_cache_template.h \
ass_font.h ass_font.c ass_fontselect.h ass_fontselect.c \
ass_render.h ass_render.c ass_render_api.c \
ass_parse.h ass_parse.c ass_shaper.h ass_shaper.c \
ass_outline.h ass_outline.c ass_drawing.h ass_drawing.c \
ass_rasterizer.h ass_rasterizer.c ass_rasterizer_c.c \
ass_bitmap.h ass_bitmap.c ass_blur.c ass_func_template.h
libass_la_LDFLAGS = -no-undefined -version-info $(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE)
libass_la_LDFLAGS += -export-symbols $(srcdir)/libass.sym
if FONTCONFIG
libass_la_SOURCES += $(SRC_FONTCONFIG)
endif
if DIRECTWRITE
libass_la_SOURCES += $(SRC_DIRECTWRITE)
endif
if CORETEXT
libass_la_SOURCES += $(SRC_CORETEXT)
endif
if ASM
if INTEL
libass_la_SOURCES += $(SRC_INTEL)
if X64
libass_la_SOURCES += $(SRC_INTEL64)
endif
endif
endif
assheadersdir = $(includedir)/ass
dist_assheaders_HEADERS = ass.h ass_types.h
EXTRA_DIST = libass.sym x86/x86inc.asm x86/utils.asm

800
libass/Makefile.in Normal file
View File

@ -0,0 +1,800 @@
# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
@FONTCONFIG_TRUE@am__append_1 = $(SRC_FONTCONFIG)
@DIRECTWRITE_TRUE@am__append_2 = $(SRC_DIRECTWRITE)
@CORETEXT_TRUE@am__append_3 = $(SRC_CORETEXT)
@ASM_TRUE@@INTEL_TRUE@am__append_4 = $(SRC_INTEL)
@ASM_TRUE@@INTEL_TRUE@@X64_TRUE@am__append_5 = $(SRC_INTEL64)
subdir = libass
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(dist_assheaders_HEADERS) \
$(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(assheadersdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libass_la_LIBADD =
am__libass_la_SOURCES_DIST = ass.h ass.c ass_types.h ass_utils.h \
ass_utils.c ass_compat.h ass_string.h ass_string.c \
ass_strtod.c ass_library.h ass_library.c ass_cache.h \
ass_cache.c ass_cache_template.h ass_font.h ass_font.c \
ass_fontselect.h ass_fontselect.c ass_render.h ass_render.c \
ass_render_api.c ass_parse.h ass_parse.c ass_shaper.h \
ass_shaper.c ass_outline.h ass_outline.c ass_drawing.h \
ass_drawing.c ass_rasterizer.h ass_rasterizer.c \
ass_rasterizer_c.c ass_bitmap.h ass_bitmap.c ass_blur.c \
ass_func_template.h ass_fontconfig.c ass_fontconfig.h \
ass_directwrite.c ass_directwrite.h dwrite_c.h ass_coretext.c \
ass_coretext.h x86/rasterizer.asm x86/blend_bitmaps.asm \
x86/blur.asm x86/cpuid.asm x86/cpuid.h x86/be_blur.asm
am__objects_1 = ass_fontconfig.lo
@FONTCONFIG_TRUE@am__objects_2 = $(am__objects_1)
am__objects_3 = ass_directwrite.lo
@DIRECTWRITE_TRUE@am__objects_4 = $(am__objects_3)
am__objects_5 = ass_coretext.lo
@CORETEXT_TRUE@am__objects_6 = $(am__objects_5)
am__dirstamp = $(am__leading_dot)dirstamp
am__objects_7 = x86/rasterizer.lo x86/blend_bitmaps.lo x86/blur.lo \
x86/cpuid.lo
@ASM_TRUE@@INTEL_TRUE@am__objects_8 = $(am__objects_7)
am__objects_9 = x86/be_blur.lo
@ASM_TRUE@@INTEL_TRUE@@X64_TRUE@am__objects_10 = $(am__objects_9)
am_libass_la_OBJECTS = ass.lo ass_utils.lo ass_string.lo ass_strtod.lo \
ass_library.lo ass_cache.lo ass_font.lo ass_fontselect.lo \
ass_render.lo ass_render_api.lo ass_parse.lo ass_shaper.lo \
ass_outline.lo ass_drawing.lo ass_rasterizer.lo \
ass_rasterizer_c.lo ass_bitmap.lo ass_blur.lo $(am__objects_2) \
$(am__objects_4) $(am__objects_6) $(am__objects_8) \
$(am__objects_10)
libass_la_OBJECTS = $(am_libass_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
libass_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libass_la_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libass_la_SOURCES)
DIST_SOURCES = $(am__libass_la_SOURCES_DIST)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(dist_assheaders_HEADERS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AS = @AS@
ASFLAGS = @ASFLAGS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCAS = @CCAS@
CCASDEPMODE = @CCASDEPMODE@
CCASFLAGS = @CCASFLAGS@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
FREETYPE_LIBS = @FREETYPE_LIBS@
FRIBIDI_CFLAGS = @FRIBIDI_CFLAGS@
FRIBIDI_LIBS = @FRIBIDI_LIBS@
GREP = @GREP@
HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
LIBPNG_LIBS = @LIBPNG_LIBS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
PKG_LIBS_DEFAULT = @PKG_LIBS_DEFAULT@
PKG_LIBS_PRIVATE = @PKG_LIBS_PRIVATE@
PKG_REQUIRES_DEFAULT = @PKG_REQUIRES_DEFAULT@
PKG_REQUIRES_PRIVATE = @PKG_REQUIRES_PRIVATE@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
nasm_check = @nasm_check@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AM_CFLAGS = -std=gnu99 -Wall -Wextra -Wno-sign-compare -Wno-unused-parameter \
-Werror-implicit-function-declaration -Wstrict-prototypes \
-Wpointer-arith -Wredundant-decls -Wno-missing-field-initializers\
-D_GNU_SOURCE
LIBASS_LT_CURRENT = 9
LIBASS_LT_REVISION = 2
LIBASS_LT_AGE = 0
nasm_verbose = $(nasm_verbose_$(V))
nasm_verbose_ = $(nasm_verbose_$(AM_DEFAULT_VERBOSITY))
nasm_verbose_0 = @echo " NASM " $@;
SRC_INTEL = x86/rasterizer.asm x86/blend_bitmaps.asm x86/blur.asm x86/cpuid.asm \
x86/cpuid.h
SRC_INTEL64 = x86/be_blur.asm
SRC_FONTCONFIG = ass_fontconfig.c ass_fontconfig.h
SRC_DIRECTWRITE = ass_directwrite.c ass_directwrite.h dwrite_c.h
SRC_CORETEXT = ass_coretext.c ass_coretext.h
lib_LTLIBRARIES = libass.la
libass_la_SOURCES = ass.h ass.c ass_types.h ass_utils.h ass_utils.c \
ass_compat.h ass_string.h ass_string.c ass_strtod.c \
ass_library.h ass_library.c ass_cache.h ass_cache.c \
ass_cache_template.h ass_font.h ass_font.c ass_fontselect.h \
ass_fontselect.c ass_render.h ass_render.c ass_render_api.c \
ass_parse.h ass_parse.c ass_shaper.h ass_shaper.c \
ass_outline.h ass_outline.c ass_drawing.h ass_drawing.c \
ass_rasterizer.h ass_rasterizer.c ass_rasterizer_c.c \
ass_bitmap.h ass_bitmap.c ass_blur.c ass_func_template.h \
$(am__append_1) $(am__append_2) $(am__append_3) \
$(am__append_4) $(am__append_5)
libass_la_LDFLAGS = -no-undefined -version-info \
$(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE) \
-export-symbols $(srcdir)/libass.sym
assheadersdir = $(includedir)/ass
dist_assheaders_HEADERS = ass.h ass_types.h
EXTRA_DIST = libass.sym x86/x86inc.asm x86/utils.asm
all: all-am
.SUFFIXES:
.SUFFIXES: .asm .c .lo .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libass/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu libass/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
list2="$$list2 $$p"; \
else :; fi; \
done; \
test -z "$$list2" || { \
echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
uninstall-libLTLIBRARIES:
@$(NORMAL_UNINSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
for p in $$list; do \
$(am__strip_dir) \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
done
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
test -z "$$locs" || { \
echo rm -f $${locs}; \
rm -f $${locs}; \
}
x86/$(am__dirstamp):
@$(MKDIR_P) x86
@: > x86/$(am__dirstamp)
x86/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) x86/$(DEPDIR)
@: > x86/$(DEPDIR)/$(am__dirstamp)
x86/rasterizer.lo: x86/$(am__dirstamp) x86/$(DEPDIR)/$(am__dirstamp)
x86/blend_bitmaps.lo: x86/$(am__dirstamp) \
x86/$(DEPDIR)/$(am__dirstamp)
x86/blur.lo: x86/$(am__dirstamp) x86/$(DEPDIR)/$(am__dirstamp)
x86/cpuid.lo: x86/$(am__dirstamp) x86/$(DEPDIR)/$(am__dirstamp)
x86/be_blur.lo: x86/$(am__dirstamp) x86/$(DEPDIR)/$(am__dirstamp)
libass.la: $(libass_la_OBJECTS) $(libass_la_DEPENDENCIES) $(EXTRA_libass_la_DEPENDENCIES)
$(AM_V_CCLD)$(libass_la_LINK) -rpath $(libdir) $(libass_la_OBJECTS) $(libass_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
-rm -f x86/*.$(OBJEXT)
-rm -f x86/*.lo
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_bitmap.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_blur.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_cache.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_coretext.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_directwrite.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_drawing.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_font.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_fontconfig.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_fontselect.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_library.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_outline.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_parse.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_rasterizer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_rasterizer_c.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_render.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_render_api.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_shaper.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_string.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_strtod.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_utils.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
-rm -rf x86/.libs x86/_libs
install-dist_assheadersHEADERS: $(dist_assheaders_HEADERS)
@$(NORMAL_INSTALL)
@list='$(dist_assheaders_HEADERS)'; test -n "$(assheadersdir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(assheadersdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(assheadersdir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(assheadersdir)'"; \
$(INSTALL_HEADER) $$files "$(DESTDIR)$(assheadersdir)" || exit $$?; \
done
uninstall-dist_assheadersHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_assheaders_HEADERS)'; test -n "$(assheadersdir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(assheadersdir)'; $(am__uninstall_files_from_dir)
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
installdirs:
for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(assheadersdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-rm -f x86/$(DEPDIR)/$(am__dirstamp)
-rm -f x86/$(am__dirstamp)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am: install-dist_assheadersHEADERS
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-libLTLIBRARIES
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-dist_assheadersHEADERS \
uninstall-libLTLIBRARIES
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dist_assheadersHEADERS install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am \
install-libLTLIBRARIES install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am \
uninstall-dist_assheadersHEADERS uninstall-libLTLIBRARIES
.PRECIOUS: Makefile
.asm.lo:
$(nasm_verbose)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(AS) $(ASFLAGS) -I$(srcdir)/ -o $@ $< -prefer-non-pic
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

1361
libass/ass.c Normal file

File diff suppressed because it is too large Load Diff

659
libass/ass.h Normal file
View File

@ -0,0 +1,659 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_ASS_H
#define LIBASS_ASS_H
#include <stdio.h>
#include <stdarg.h>
#include "ass_types.h"
#define LIBASS_VERSION 0x01400000
#ifdef __cplusplus
extern "C" {
#endif
/*
* A linked list of images produced by an ass renderer.
*
* These images have to be rendered in-order for the correct screen
* composition. The libass renderer clips these bitmaps to the frame size.
* w/h can be zero, in this case the bitmap should not be rendered at all.
* The last bitmap row is not guaranteed to be padded up to stride size,
* e.g. in the worst case a bitmap has the size stride * (h - 1) + w.
*/
typedef struct ass_image {
int w, h; // Bitmap width/height
int stride; // Bitmap stride
unsigned char *bitmap; // 1bpp stride*h alpha buffer
// Note: the last row may not be padded to
// bitmap stride!
uint32_t color; // Bitmap color and alpha, RGBA
int dst_x, dst_y; // Bitmap placement inside the video frame
struct ass_image *next; // Next image, or NULL
enum {
IMAGE_TYPE_CHARACTER,
IMAGE_TYPE_OUTLINE,
IMAGE_TYPE_SHADOW
} type;
} ASS_Image;
/*
* Hinting type. (see ass_set_hinting below)
*
* Setting hinting to anything but ASS_HINTING_NONE will put libass in a mode
* that reduces compatibility with vsfilter and many ASS scripts. The main
* problem is that hinting conflicts with smooth scaling, which precludes
* animations and precise positioning.
*
* In other words, enabling hinting might break some scripts severely.
*
* FreeType's native hinter is still buggy sometimes and it is recommended
* to use the light autohinter, ASS_HINTING_LIGHT, instead. For best
* compatibility with problematic fonts, disable hinting.
*/
typedef enum {
ASS_HINTING_NONE = 0,
ASS_HINTING_LIGHT,
ASS_HINTING_NORMAL,
ASS_HINTING_NATIVE
} ASS_Hinting;
/**
* \brief Text shaping levels.
*
* SIMPLE is a fast, font-agnostic shaper that can do only substitutions.
* COMPLEX is a slower shaper using OpenType for substitutions and positioning.
*
* libass uses the best shaper available by default.
*/
typedef enum {
ASS_SHAPING_SIMPLE = 0,
ASS_SHAPING_COMPLEX
} ASS_ShapingLevel;
/**
* \brief Style override options. See
* ass_set_selective_style_override_enabled() for details.
*/
typedef enum {
/**
* Default mode (with no other bits set). All selective override features
* as well as the style set with ass_set_selective_style_override() are
* disabled, but traditional overrides like ass_set_font_scale() are
* applied unconditionally.
*/
ASS_OVERRIDE_DEFAULT = 0,
/**
* Apply the style as set with ass_set_selective_style_override() on events
* which look like dialogue. Other style overrides are also applied this
* way, except ass_set_font_scale(). How ass_set_font_scale() is applied
* depends on the ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE flag.
*
* This is equivalent to setting all of the following bits:
*
* ASS_OVERRIDE_BIT_FONT_NAME
* ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS
* ASS_OVERRIDE_BIT_COLORS
* ASS_OVERRIDE_BIT_BORDER
* ASS_OVERRIDE_BIT_ATTRIBUTES
*/
ASS_OVERRIDE_BIT_STYLE = 1 << 0,
/**
* Apply ass_set_font_scale() only on events which look like dialogue.
* If not set, the font scale is applied to all events. (The behavior and
* name of this flag are unintuitive, but exist for compatibility)
*/
ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE = 1 << 1,
/**
* Old alias for ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE. Deprecated. Do not use.
*/
ASS_OVERRIDE_BIT_FONT_SIZE = 1 << 1,
/**
* On dialogue events override: FontSize, Spacing, Blur, ScaleX, ScaleY
*/
ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS = 1 << 2,
/**
* On dialogue events override: FontName, treat_fontname_as_pattern
*/
ASS_OVERRIDE_BIT_FONT_NAME = 1 << 3,
/**
* On dialogue events override: PrimaryColour, SecondaryColour, OutlineColour, BackColour
*/
ASS_OVERRIDE_BIT_COLORS = 1 << 4,
/**
* On dialogue events override: Bold, Italic, Underline, StrikeOut
*/
ASS_OVERRIDE_BIT_ATTRIBUTES = 1 << 5,
/**
* On dialogue events override: BorderStyle, Outline, Shadow
*/
ASS_OVERRIDE_BIT_BORDER = 1 << 6,
/**
* On dialogue events override: Alignment
*/
ASS_OVERRIDE_BIT_ALIGNMENT = 1 << 7,
/**
* On dialogue events override: MarginL, MarginR, MarginV
*/
ASS_OVERRIDE_BIT_MARGINS = 1 << 8,
/**
* Unconditionally replace all fields of all styles with the one provided
* with ass_set_selective_style_override().
* Does not apply ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE.
* Add ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS and ASS_OVERRIDE_BIT_BORDER if
* you want FontSize, Spacing, Outline, Shadow to be scaled to the script
* resolution given by the ASS_Track.
*/
ASS_OVERRIDE_FULL_STYLE = 1 << 9,
/**
* On dialogue events override: Justify
*/
ASS_OVERRIDE_BIT_JUSTIFY = 1 << 10,
} ASS_OverrideBits;
/**
* \brief Return the version of library. This returns the value LIBASS_VERSION
* was set to when the library was compiled.
* \return library version
*/
int ass_library_version(void);
/**
* \brief Default Font provider to load fonts in libass' database
*
* NONE don't use any default font provider for font lookup
* AUTODETECT use the first available font provider
* CORETEXT force a CoreText based font provider (OS X only)
* FONTCONFIG force a Fontconfig based font provider
*
* libass uses the best shaper available by default.
*/
typedef enum {
ASS_FONTPROVIDER_NONE = 0,
ASS_FONTPROVIDER_AUTODETECT = 1,
ASS_FONTPROVIDER_CORETEXT,
ASS_FONTPROVIDER_FONTCONFIG,
ASS_FONTPROVIDER_DIRECTWRITE,
} ASS_DefaultFontProvider;
/**
* \brief Initialize the library.
* \return library handle or NULL if failed
*/
ASS_Library *ass_library_init(void);
/**
* \brief Finalize the library
* \param priv library handle
*/
void ass_library_done(ASS_Library *priv);
/**
* \brief Set additional fonts directory.
* Optional directory that will be scanned for fonts recursively. The fonts
* found are used for font lookup.
* NOTE: A valid font directory is not needed to support embedded fonts.
*
* \param priv library handle
* \param fonts_dir directory with additional fonts
*/
void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir);
/**
* \brief Whether fonts should be extracted from track data.
* \param priv library handle
* \param extract whether to extract fonts
*/
void ass_set_extract_fonts(ASS_Library *priv, int extract);
/**
* \brief Register style overrides with a library instance.
* The overrides should have the form [Style.]Param=Value, e.g.
* SomeStyle.Font=Arial
* ScaledBorderAndShadow=yes
*
* \param priv library handle
* \param list NULL-terminated list of strings
*/
void ass_set_style_overrides(ASS_Library *priv, char **list);
/**
* \brief Explicitly process style overrides for a track.
* \param track track handle
*/
void ass_process_force_style(ASS_Track *track);
/**
* \brief Register a callback for debug/info messages.
* If a callback is registered, it is called for every message emitted by
* libass. The callback receives a format string and a list of arguments,
* to be used for the printf family of functions. Additionally, a log level
* from 0 (FATAL errors) to 7 (verbose DEBUG) is passed. Usually, level 5
* should be used by applications.
* If no callback is set, all messages level < 5 are printed to stderr,
* prefixed with [ass].
*
* \param priv library handle
* \param msg_cb pointer to callback function
* \param data additional data, will be passed to callback
*/
void ass_set_message_cb(ASS_Library *priv, void (*msg_cb)
(int level, const char *fmt, va_list args, void *data),
void *data);
/**
* \brief Initialize the renderer.
* \param priv library handle
* \return renderer handle or NULL if failed
*/
ASS_Renderer *ass_renderer_init(ASS_Library *);
/**
* \brief Finalize the renderer.
* \param priv renderer handle
*/
void ass_renderer_done(ASS_Renderer *priv);
/**
* \brief Set the frame size in pixels, including margins.
* The renderer will never return images that are outside of the frame area.
* The value set with this function can influence the pixel aspect ratio used
* for rendering. If the frame size doesn't equal to the video size, you may
* have to use ass_set_pixel_aspect().
* @see ass_set_pixel_aspect()
* @see ass_set_margins()
* \param priv renderer handle
* \param w width
* \param h height
*/
void ass_set_frame_size(ASS_Renderer *priv, int w, int h);
/**
* \brief Set the source image size in pixels.
* This is used to calculate the source aspect ratio and the blur scale.
* The source image size can be reset to default by setting w and h to 0.
* The value set with this function can influence the pixel aspect ratio used
* for rendering.
* @see ass_set_pixel_aspect()
* \param priv renderer handle
* \param w width
* \param h height
*/
void ass_set_storage_size(ASS_Renderer *priv, int w, int h);
/**
* \brief Set shaping level. This is merely a hint, the renderer will use
* whatever is available if the request cannot be fulfilled.
* \param level shaping level
*/
void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level);
/**
* \brief Set frame margins. These values may be negative if pan-and-scan
* is used. The margins are in pixels. Each value specifies the distance from
* the video rectangle to the renderer frame. If a given margin value is
* positive, there will be free space between renderer frame and video area.
* If a given margin value is negative, the frame is inside the video, i.e.
* the video has been cropped.
*
* The renderer will try to keep subtitles inside the frame area. If possible,
* text is layout so that it is inside the cropped area. Subtitle events
* that can't be moved are cropped against the frame area.
*
* ass_set_use_margins() can be used to allow libass to render subtitles into
* the empty areas if margins are positive, i.e. the video area is smaller than
* the frame. (Traditionally, this has been used to show subtitles in
* the bottom "black bar" between video bottom screen border when playing 16:9
* video on a 4:3 screen.)
*
* When using this function, it is recommended to calculate and set your own
* aspect ratio with ass_set_pixel_aspect(), as the defaults won't make any
* sense.
* @see ass_set_pixel_aspect()
* \param priv renderer handle
* \param t top margin
* \param b bottom margin
* \param l left margin
* \param r right margin
*/
void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r);
/**
* \brief Whether margins should be used for placing regular events.
* \param priv renderer handle
* \param use whether to use the margins
*/
void ass_set_use_margins(ASS_Renderer *priv, int use);
/**
* \brief Set pixel aspect ratio correction.
* This is the ratio of pixel width to pixel height.
*
* Generally, this is (s_w / s_h) / (d_w / d_h), where s_w and s_h is the
* video storage size, and d_w and d_h is the video display size. (Display
* and storage size can be different for anamorphic video, such as DVDs.)
*
* If the pixel aspect ratio is 0, or if the aspect ratio has never been set
* by calling this function, libass will calculate a default pixel aspect ratio
* out of values set with ass_set_frame_size() and ass_set_storage_size(). Note
* that this is useful only if the frame size corresponds to the video display
* size. Keep in mind that the margins set with ass_set_margins() are ignored
* for aspect ratio calculations as well.
* If the storage size has not been set, a pixel aspect ratio of 1 is assumed.
* \param priv renderer handle
* \param par pixel aspect ratio (1.0 means square pixels, 0 means default)
*/
void ass_set_pixel_aspect(ASS_Renderer *priv, double par);
/**
* \brief Set aspect ratio parameters.
* This calls ass_set_pixel_aspect(priv, dar / sar).
* @deprecated New code should use ass_set_pixel_aspect().
* \param priv renderer handle
* \param dar display aspect ratio (DAR), prescaled for output PAR
* \param sar storage aspect ratio (SAR)
*/
void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar);
/**
* \brief Set a fixed font scaling factor.
* \param priv renderer handle
* \param font_scale scaling factor, default is 1.0
*/
void ass_set_font_scale(ASS_Renderer *priv, double font_scale);
/**
* \brief Set font hinting method.
* \param priv renderer handle
* \param ht hinting method
*/
void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht);
/**
* \brief Set line spacing. Will not be scaled with frame size.
* \param priv renderer handle
* \param line_spacing line spacing in pixels
*/
void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing);
/**
* \brief Set vertical line position.
* \param priv renderer handle
* \param line_position vertical line position of subtitles in percent
* (0-100: 0 = on the bottom (default), 100 = on top)
*/
void ass_set_line_position(ASS_Renderer *priv, double line_position);
/**
* \brief Get the list of available font providers. The output array
* is allocated with malloc and can be released with free(). If an
* allocation error occurs, size is set to (size_t)-1.
* \param priv library handle
* \param providers output, list of default providers (malloc'ed array)
* \param size output, number of providers
* \return list of available font providers (user owns the returned array)
*/
void ass_get_available_font_providers(ASS_Library *priv,
ASS_DefaultFontProvider **providers,
size_t *size);
/**
* \brief Set font lookup defaults.
* \param default_font path to default font to use. Must be supplied if
* fontconfig is disabled or unavailable.
* \param default_family fallback font family for fontconfig, or NULL
* \param dfp which font provider to use (one of ASS_DefaultFontProvider). In
* older libass version, this could be 0 or 1, where 1 enabled fontconfig.
* Newer relases also accept 0 (ASS_FONTPROVIDER_NONE) and 1
* (ASS_FONTPROVIDER_AUTODETECT), which is almost backward-compatible.
* If the requested fontprovider does not exist or fails to initialize, the
* behavior is the same as when ASS_FONTPROVIDER_NONE was passed.
* \param config path to fontconfig configuration file, or NULL. Only relevant
* if fontconfig is used.
* \param update whether fontconfig cache should be built/updated now. Only
* relevant if fontconfig is used.
*
* NOTE: font lookup must be configured before an ASS_Renderer can be used.
*/
void ass_set_fonts(ASS_Renderer *priv, const char *default_font,
const char *default_family, int dfp,
const char *config, int update);
/**
* \brief Set selective style override mode.
* If enabled, the renderer attempts to override the ASS script's styling of
* normal subtitles, without affecting explicitly positioned text. If an event
* looks like a normal subtitle, parts of the font style are copied from the
* user style set with ass_set_selective_style_override().
* Warning: the heuristic used for deciding when to override the style is rather
* rough, and enabling this option can lead to incorrectly rendered
* subtitles. Since the ASS format doesn't have any support for
* allowing end-users to customize subtitle styling, this feature can
* only be implemented on "best effort" basis, and has to rely on
* heuristics that can easily break.
* \param priv renderer handle
* \param bits bit mask comprised of ASS_OverrideBits values.
*/
void ass_set_selective_style_override_enabled(ASS_Renderer *priv, int bits);
/**
* \brief Set style for selective style override.
* See ass_set_selective_style_override_enabled().
* \param style style settings to use if override is enabled. Applications
* should initialize it with {0} before setting fields. Strings will be copied
* by the function.
*/
void ass_set_selective_style_override(ASS_Renderer *priv, ASS_Style *style);
/**
* \brief This is a stub and does nothing. Old documentation: Update/build font
* cache. This needs to be called if it was disabled when ass_set_fonts was set.
*
* \param priv renderer handle
* \return success
*/
int ass_fonts_update(ASS_Renderer *priv);
/**
* \brief Set hard cache limits. Do not set, or set to zero, for reasonable
* defaults.
*
* \param priv renderer handle
* \param glyph_max maximum number of cached glyphs
* \param bitmap_max_size maximum bitmap cache size (in MB)
*/
void ass_set_cache_limits(ASS_Renderer *priv, int glyph_max,
int bitmap_max_size);
/**
* \brief Render a frame, producing a list of ASS_Image.
* \param priv renderer handle
* \param track subtitle track
* \param now video timestamp in milliseconds
* \param detect_change compare to the previous call and set to 1
* if positions changed, or set to 2 if content changed.
*/
ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track,
long long now, int *detect_change);
/*
* The following functions operate on track objects and do not need
* an ass_renderer
*/
/**
* \brief Allocate a new empty track object.
* \param library handle
* \return pointer to empty track
*/
ASS_Track *ass_new_track(ASS_Library *);
/**
* \brief Deallocate track and all its child objects (styles and events).
* \param track track to deallocate
*/
void ass_free_track(ASS_Track *track);
/**
* \brief Allocate new style.
* \param track track
* \return newly allocated style id
*/
int ass_alloc_style(ASS_Track *track);
/**
* \brief Allocate new event.
* \param track track
* \return newly allocated event id
*/
int ass_alloc_event(ASS_Track *track);
/**
* \brief Delete a style.
* \param track track
* \param sid style id
* Deallocates style data. Does not modify track->n_styles.
*/
void ass_free_style(ASS_Track *track, int sid);
/**
* \brief Delete an event.
* \param track track
* \param eid event id
* Deallocates event data. Does not modify track->n_events.
*/
void ass_free_event(ASS_Track *track, int eid);
/**
* \brief Parse a chunk of subtitle stream data.
* \param track track
* \param data string to parse
* \param size length of data
*/
void ass_process_data(ASS_Track *track, char *data, int size);
/**
* \brief Parse Codec Private section of the subtitle stream, in Matroska
* format. See the Matroska specification for details.
* \param track target track
* \param data string to parse
* \param size length of data
*/
void ass_process_codec_private(ASS_Track *track, char *data, int size);
/**
* \brief Parse a chunk of subtitle stream data. A chunk contains exactly one
* event in Matroska format. See the Matroska specification for details.
* In later libass versions (since LIBASS_VERSION==0x01300001), using this
* function means you agree not to modify events manually, or using other
* functions manipulating the event list like ass_process_data(). If you do
* anyway, the internal duplicate checking might break. Calling
* ass_flush_events() is still allowed.
* \param track track
* \param data string to parse
* \param size length of data
* \param timecode starting time of the event (milliseconds)
* \param duration duration of the event (milliseconds)
*/
void ass_process_chunk(ASS_Track *track, char *data, int size,
long long timecode, long long duration);
/**
* \brief Set whether the ReadOrder field when processing a packet with
* ass_process_chunk() should be used for eliminating duplicates.
* \param check_readorder 0 means do not try to eliminate duplicates; 1 means
* use the ReadOrder field embedded in the packet as unique identifier, and
* discard the packet if there was already a packet with the same ReadOrder.
* Other values are undefined.
* If this function is not called, the default value is 1.
*/
void ass_set_check_readorder(ASS_Track *track, int check_readorder);
/**
* \brief Flush buffered events.
* \param track track
*/
void ass_flush_events(ASS_Track *track);
/**
* \brief Read subtitles from file.
* \param library library handle
* \param fname file name
* \param codepage encoding (iconv format)
* \return newly allocated track
*/
ASS_Track *ass_read_file(ASS_Library *library, char *fname,
char *codepage);
/**
* \brief Read subtitles from memory.
* \param library library handle
* \param buf pointer to subtitles text
* \param bufsize size of buffer
* \param codepage encoding (iconv format)
* \return newly allocated track
*/
ASS_Track *ass_read_memory(ASS_Library *library, char *buf,
size_t bufsize, char *codepage);
/**
* \brief Read styles from file into already initialized track.
* \param fname file name
* \param codepage encoding (iconv format)
* \return 0 on success
*/
int ass_read_styles(ASS_Track *track, char *fname, char *codepage);
/**
* \brief Add a memory font.
* \param library library handle
* \param name attachment name
* \param data binary font data
* \param data_size data size
*/
void ass_add_font(ASS_Library *library, char *name, char *data,
int data_size);
/**
* \brief Remove all fonts stored in an ass_library object.
* \param library library handle
*/
void ass_clear_fonts(ASS_Library *library);
/**
* \brief Calculates timeshift from now to the start of some other subtitle
* event, depending on movement parameter.
* \param track subtitle track
* \param now current time in milliseconds
* \param movement how many events to skip from the one currently displayed
* +2 means "the one after the next", -1 means "previous"
* \return timeshift in milliseconds
*/
long long ass_step_sub(ASS_Track *track, long long now, int movement);
#ifdef __cplusplus
}
#endif
#endif /* LIBASS_ASS_H */

517
libass/ass_bitmap.c Normal file
View File

@ -0,0 +1,517 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
* Copyright (c) 2011-2014, Yu Zhuohuang <yuzhuohuang@qq.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <assert.h>
#include <ft2build.h>
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include "ass_utils.h"
#include "ass_outline.h"
#include "ass_bitmap.h"
#include "ass_render.h"
#define ALIGN C_ALIGN_ORDER
#define DECORATE(func) ass_##func##_c
#include "ass_func_template.h"
#undef ALIGN
#undef DECORATE
#if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
#define ALIGN 4
#define DECORATE(func) ass_##func##_sse2
#include "ass_func_template.h"
#undef ALIGN
#undef DECORATE
#define ALIGN 5
#define DECORATE(func) ass_##func##_avx2
#include "ass_func_template.h"
#undef ALIGN
#undef DECORATE
#endif
void ass_synth_blur(const BitmapEngine *engine, int opaque_box, int be,
double blur_radius, Bitmap *bm_g, Bitmap *bm_o)
{
bool blur_g = !bm_o || opaque_box;
if (blur_g && !bm_g)
return;
// Apply gaussian blur
double r2 = blur_radius * blur_radius / log(256);
if (r2 > 0.001) {
if (bm_o)
ass_gaussian_blur(engine, bm_o, r2);
if (blur_g)
ass_gaussian_blur(engine, bm_g, r2);
}
// Apply box blur (multiple passes, if requested)
if (be) {
size_t size_o = 0, size_g = 0;
if (bm_o)
size_o = sizeof(uint16_t) * bm_o->stride * 2;
if (blur_g)
size_g = sizeof(uint16_t) * bm_g->stride * 2;
size_t size = FFMAX(size_o, size_g);
uint16_t *tmp = size ? ass_aligned_alloc(32, size, false) : NULL;
if (!tmp)
return;
if (bm_o) {
unsigned passes = be;
unsigned w = bm_o->w;
unsigned h = bm_o->h;
unsigned stride = bm_o->stride;
unsigned char *buf = bm_o->buffer;
if(w && h){
if(passes > 1){
be_blur_pre(buf, w, h, stride);
while(--passes){
memset(tmp, 0, stride * 2);
engine->be_blur(buf, w, h, stride, tmp);
}
be_blur_post(buf, w, h, stride);
}
memset(tmp, 0, stride * 2);
engine->be_blur(buf, w, h, stride, tmp);
}
}
if (blur_g) {
unsigned passes = be;
unsigned w = bm_g->w;
unsigned h = bm_g->h;
unsigned stride = bm_g->stride;
unsigned char *buf = bm_g->buffer;
if(w && h){
if(passes > 1){
be_blur_pre(buf, w, h, stride);
while(--passes){
memset(tmp, 0, stride * 2);
engine->be_blur(buf, w, h, stride, tmp);
}
be_blur_post(buf, w, h, stride);
}
memset(tmp, 0, stride * 2);
engine->be_blur(buf, w, h, stride, tmp);
}
}
ass_aligned_free(tmp);
}
}
static bool alloc_bitmap_buffer(const BitmapEngine *engine, Bitmap *bm, int w, int h,
bool zero)
{
unsigned align = 1 << engine->align_order;
size_t s = ass_align(align, w);
// Too often we use ints as offset for bitmaps => use INT_MAX.
if (s > (INT_MAX - 32) / FFMAX(h, 1))
return false;
uint8_t *buf = ass_aligned_alloc(align, s * h + 32, zero);
if (!buf)
return false;
bm->w = w;
bm->h = h;
bm->stride = s;
bm->buffer = buf;
return true;
}
Bitmap *alloc_bitmap(const BitmapEngine *engine, int w, int h, bool zero)
{
Bitmap *bm = malloc(sizeof(Bitmap));
if (!bm)
return NULL;
if (!alloc_bitmap_buffer(engine, bm, w, h, zero)) {
free(bm);
return NULL;
}
bm->left = bm->top = 0;
return bm;
}
bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int w, int h)
{
uint8_t *old = bm->buffer;
if (!alloc_bitmap_buffer(engine, bm, w, h, false))
return false;
ass_aligned_free(old);
return true;
}
void ass_free_bitmap(Bitmap *bm)
{
if (bm)
ass_aligned_free(bm->buffer);
free(bm);
}
Bitmap *copy_bitmap(const BitmapEngine *engine, const Bitmap *src)
{
Bitmap *dst = alloc_bitmap(engine, src->w, src->h, false);
if (!dst)
return NULL;
dst->left = src->left;
dst->top = src->top;
memcpy(dst->buffer, src->buffer, src->stride * src->h);
return dst;
}
Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
ASS_Outline *outline1, ASS_Outline *outline2,
int bord)
{
RasterizerData *rst = &render_priv->rasterizer;
if (outline1 && !rasterizer_set_outline(rst, outline1, false)) {
ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
return NULL;
}
if (outline2 && !rasterizer_set_outline(rst, outline2, !!outline1)) {
ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
return NULL;
}
if (bord < 0 || bord > INT_MAX / 2)
return NULL;
if (rst->bbox.x_max > INT_MAX - 63 || rst->bbox.y_max > INT_MAX - 63)
return NULL;
int x_min = rst->bbox.x_min >> 6;
int y_min = rst->bbox.y_min >> 6;
int x_max = (rst->bbox.x_max + 63) >> 6;
int y_max = (rst->bbox.y_max + 63) >> 6;
int w = x_max - x_min;
int h = y_max - y_min;
int mask = (1 << render_priv->engine->tile_order) - 1;
if (w < 0 || h < 0 ||
w > INT_MAX - (2 * bord + mask) || h > INT_MAX - (2 * bord + mask)) {
ass_msg(render_priv->library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx",
w, h);
return NULL;
}
int tile_w = (w + 2 * bord + mask) & ~mask;
int tile_h = (h + 2 * bord + mask) & ~mask;
Bitmap *bm = alloc_bitmap(render_priv->engine, tile_w, tile_h, false);
if (!bm)
return NULL;
bm->left = x_min - bord;
bm->top = y_min - bord;
if (!rasterizer_fill(render_priv->engine, rst, bm->buffer,
x_min - bord, y_min - bord,
bm->stride, tile_h, bm->stride)) {
ass_msg(render_priv->library, MSGL_WARN, "Failed to rasterize glyph!\n");
ass_free_bitmap(bm);
return NULL;
}
return bm;
}
/**
* \brief fix outline bitmap
*
* The glyph bitmap is subtracted from outline bitmap. This way looks much
* better in some cases.
*/
void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
{
int x, y;
const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left;
const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top;
const int r =
bm_o->left + bm_o->stride <
bm_g->left + bm_g->stride ? bm_o->left + bm_o->stride : bm_g->left + bm_g->stride;
const int b =
bm_o->top + bm_o->h <
bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h;
unsigned char *g =
bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left);
unsigned char *o =
bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left);
for (y = 0; y < b - t; ++y) {
for (x = 0; x < r - l; ++x) {
unsigned char c_g, c_o;
c_g = g[x];
c_o = o[x];
o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0;
}
g += bm_g->stride;
o += bm_o->stride;
}
}
/**
* \brief Shift a bitmap by the fraction of a pixel in x and y direction
* expressed in 26.6 fixed point
*/
void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
{
int x, y, b;
int w = bm->w;
int h = bm->h;
int s = bm->stride;
unsigned char *buf = bm->buffer;
assert((shift_x & ~63) == 0 && (shift_y & ~63) == 0);
// Shift in x direction
for (y = 0; y < h; y++) {
for (x = w - 1; x > 0; x--) {
b = (buf[x + y * s - 1] * shift_x) >> 6;
buf[x + y * s - 1] -= b;
buf[x + y * s] += b;
}
}
// Shift in y direction
for (x = 0; x < w; x++) {
for (y = h - 1; y > 0; y--) {
b = (buf[x + (y - 1) * s] * shift_y) >> 6;
buf[x + (y - 1) * s] -= b;
buf[x + y * s] += b;
}
}
}
/**
* \brief Blur with [[1,2,1], [2,4,2], [1,2,1]] kernel
* This blur is the same as the one employed by vsfilter.
* Pure C implementation.
*/
void ass_be_blur_c(uint8_t *buf, intptr_t w, intptr_t h,
intptr_t stride, uint16_t *tmp)
{
uint16_t *col_pix_buf = tmp;
uint16_t *col_sum_buf = tmp + w;
unsigned x, y, old_pix, old_sum, temp1, temp2;
uint8_t *src, *dst;
memset(tmp, 0, sizeof(uint16_t) * w * 2);
y = 0;
{
src=buf+y*stride;
x = 1;
old_pix = src[x-1];
old_sum = old_pix;
for ( ; x < w; x++) {
temp1 = src[x];
temp2 = old_pix + temp1;
old_pix = temp1;
temp1 = old_sum + temp2;
old_sum = temp2;
col_pix_buf[x-1] = temp1;
col_sum_buf[x-1] = temp1;
}
temp1 = old_sum + old_pix;
col_pix_buf[x-1] = temp1;
col_sum_buf[x-1] = temp1;
}
for (y++; y < h; y++) {
src=buf+y*stride;
dst=buf+(y-1)*stride;
x = 1;
old_pix = src[x-1];
old_sum = old_pix;
for ( ; x < w; x++) {
temp1 = src[x];
temp2 = old_pix + temp1;
old_pix = temp1;
temp1 = old_sum + temp2;
old_sum = temp2;
temp2 = col_pix_buf[x-1] + temp1;
col_pix_buf[x-1] = temp1;
dst[x-1] = (col_sum_buf[x-1] + temp2) >> 4;
col_sum_buf[x-1] = temp2;
}
temp1 = old_sum + old_pix;
temp2 = col_pix_buf[x-1] + temp1;
col_pix_buf[x-1] = temp1;
dst[x-1] = (col_sum_buf[x-1] + temp2) >> 4;
col_sum_buf[x-1] = temp2;
}
{
dst=buf+(y-1)*stride;
for (x = 0; x < w; x++)
dst[x] = (col_sum_buf[x] + col_pix_buf[x]) >> 4;
}
}
void be_blur_pre(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
{
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
// This is equivalent to (value * 64 + 127) / 255 for all
// values from 0 to 256 inclusive. Assist vectorizing
// compilers by noting that all temporaries fit in 8 bits.
buf[y * stride + x] =
(uint8_t) ((buf[y * stride + x] >> 1) + 1) >> 1;
}
}
}
void be_blur_post(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
{
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
// This is equivalent to (value * 255 + 32) / 64 for all values
// from 0 to 96 inclusive, and we only care about 0 to 64.
uint8_t value = buf[y * stride + x];
buf[y * stride + x] = (value << 2) - (value > 32);
}
}
}
/*
* To find these values, simulate blur on the border between two
* half-planes, one zero-filled (background) and the other filled
* with the maximum supported value (foreground). Keep incrementing
* the \be argument. The necessary padding is the distance by which
* the blurred foreground image extends beyond the original border
* and into the background. Initially it increases along with \be,
* but very soon it grinds to a halt. At some point, the blurred
* image actually reaches a stationary point and stays unchanged
* forever after, simply _shifting_ by one pixel for each \be
* step--moving in the direction of the non-zero half-plane and
* thus decreasing the necessary padding (although the large
* padding is still needed for intermediate results). In practice,
* images are finite rather than infinite like half-planes, but
* this can only decrease the required padding. Half-planes filled
* with extreme values are the theoretical limit of the worst case.
* Make sure to use the right pixel value range in the simulation!
*/
int be_padding(int be)
{
if (be <= 3)
return be;
if (be <= 7)
return 4;
if (be <= 123)
return 5;
return FFMAX(128 - be, 0);
}
bool outline_to_bitmap2(ASS_Renderer *render_priv, ASS_Outline *outline,
ASS_Outline *border1, ASS_Outline *border2,
Bitmap **bm_g, Bitmap **bm_o)
{
assert(bm_g && bm_o);
*bm_g = *bm_o = NULL;
if (outline && !outline->n_points)
outline = NULL;
if (border1 && !border1->n_points)
border1 = NULL;
if (border2 && !border2->n_points)
border2 = NULL;
if (outline) {
*bm_g = outline_to_bitmap(render_priv, outline, NULL, 1);
if (!*bm_g)
return false;
}
if (border1 || border2) {
*bm_o = outline_to_bitmap(render_priv, border1, border2, 1);
if (!*bm_o) {
return false;
}
}
return true;
}
/**
* \brief Add two bitmaps together at a given position
* Uses additive blending, clipped to [0,255]. Pure C implementation.
*/
void ass_add_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
uint8_t *src, intptr_t src_stride,
intptr_t height, intptr_t width)
{
unsigned out;
uint8_t* end = dst + dst_stride * height;
while (dst < end) {
for (unsigned j = 0; j < width; ++j) {
out = dst[j] + src[j];
dst[j] = FFMIN(out, 255);
}
dst += dst_stride;
src += src_stride;
}
}
void ass_sub_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
uint8_t *src, intptr_t src_stride,
intptr_t height, intptr_t width)
{
short out;
uint8_t* end = dst + dst_stride * height;
while (dst < end) {
for (unsigned j = 0; j < width; ++j) {
out = dst[j] - src[j];
dst[j] = FFMAX(out, 0);
}
dst += dst_stride;
src += src_stride;
}
}
void ass_mul_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
uint8_t *src1, intptr_t src1_stride,
uint8_t *src2, intptr_t src2_stride,
intptr_t w, intptr_t h)
{
uint8_t* end = src1 + src1_stride * h;
while (src1 < end) {
for (unsigned x = 0; x < w; ++x) {
dst[x] = (src1[x] * src2[x] + 255) >> 8;
}
dst += dst_stride;
src1 += src1_stride;
src2 += src2_stride;
}
}

132
libass/ass_bitmap.h Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_BITMAP_H
#define LIBASS_BITMAP_H
#include <stdbool.h>
#include <ft2build.h>
#include FT_GLYPH_H
#include "ass.h"
#include "ass_outline.h"
struct segment;
typedef void (*FillSolidTileFunc)(uint8_t *buf, ptrdiff_t stride, int set);
typedef void (*FillHalfplaneTileFunc)(uint8_t *buf, ptrdiff_t stride,
int32_t a, int32_t b, int64_t c, int32_t scale);
typedef void (*FillGenericTileFunc)(uint8_t *buf, ptrdiff_t stride,
const struct segment *line, size_t n_lines,
int winding);
typedef void (*BitmapBlendFunc)(uint8_t *dst, intptr_t dst_stride,
uint8_t *src, intptr_t src_stride,
intptr_t height, intptr_t width);
typedef void (*BitmapMulFunc)(uint8_t *dst, intptr_t dst_stride,
uint8_t *src1, intptr_t src1_stride,
uint8_t *src2, intptr_t src2_stride,
intptr_t width, intptr_t height);
typedef void (*BeBlurFunc)(uint8_t *buf, intptr_t w, intptr_t h,
intptr_t stride, uint16_t *tmp);
// intermediate bitmaps represented as sets of verical stripes of int16_t[alignment / 2]
typedef void (*Convert8to16Func)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride,
uintptr_t width, uintptr_t height);
typedef void (*Convert16to8Func)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src,
uintptr_t width, uintptr_t height);
typedef void (*FilterFunc)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
typedef void (*ParamFilterFunc)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param);
#define C_ALIGN_ORDER 5
typedef struct {
int align_order; // log2(alignment)
// rasterizer functions
int tile_order; // log2(tile_size)
FillSolidTileFunc fill_solid;
FillHalfplaneTileFunc fill_halfplane;
FillGenericTileFunc fill_generic;
// blend functions
BitmapBlendFunc add_bitmaps, sub_bitmaps;
BitmapMulFunc mul_bitmaps;
// be blur function
BeBlurFunc be_blur;
// gaussian blur functions
Convert8to16Func stripe_unpack;
Convert16to8Func stripe_pack;
FilterFunc shrink_horz, shrink_vert;
FilterFunc expand_horz, expand_vert;
FilterFunc pre_blur_horz[3], pre_blur_vert[3];
ParamFilterFunc main_blur_horz[3], main_blur_vert[3];
} BitmapEngine;
extern const BitmapEngine ass_bitmap_engine_c;
extern const BitmapEngine ass_bitmap_engine_sse2;
extern const BitmapEngine ass_bitmap_engine_avx2;
typedef struct {
int left, top;
int w, h; // width, height
int stride;
unsigned char *buffer; // h * stride buffer
} Bitmap;
Bitmap *alloc_bitmap(const BitmapEngine *engine, int w, int h, bool zero);
bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int w, int h);
Bitmap *copy_bitmap(const BitmapEngine *engine, const Bitmap *src);
void ass_free_bitmap(Bitmap *bm);
Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
ASS_Outline *outline1, ASS_Outline *outline2,
int bord);
void ass_synth_blur(const BitmapEngine *engine, int opaque_box, int be,
double blur_radius, Bitmap *bm_g, Bitmap *bm_o);
/**
* \brief perform glyph rendering
* \param outline original glyph
* \param border1 inside "border" outline, produced by stroker
* \param border2 outside "border" outline, produced by stroker
* \param bm_g out: pointer to the bitmap of original glyph is returned here
* \param bm_o out: pointer to the bitmap of border glyph is returned here
*/
bool outline_to_bitmap2(ASS_Renderer *render_priv, ASS_Outline *outline,
ASS_Outline *border1, ASS_Outline *border2,
Bitmap **bm_g, Bitmap **bm_o);
int be_padding(int be);
void be_blur_pre(uint8_t *buf, intptr_t w,
intptr_t h, intptr_t stride);
void be_blur_post(uint8_t *buf, intptr_t w,
intptr_t h, intptr_t stride);
bool ass_gaussian_blur(const BitmapEngine *engine, Bitmap *bm, double r2);
void shift_bitmap(Bitmap *bm, int shift_x, int shift_y);
void fix_outline(Bitmap *bm_g, Bitmap *bm_o);
#endif /* LIBASS_BITMAP_H */

912
libass/ass_blur.c Normal file
View File

@ -0,0 +1,912 @@
/*
* Copyright (C) 2015 Vabishchevich Nikolay <vabnick@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <math.h>
#include <stdbool.h>
#include "ass_utils.h"
#include "ass_bitmap.h"
/*
* Cascade Blur Algorithm
*
* The main idea is simple: to approximate gaussian blur with large radius
* you can downscale, then apply filter with small pattern, then upscale back.
*
* To achieve desired precision down/upscaling should be done with sufficiently smooth kernel.
* Experiment shows that downscaling of factor 2 with kernel [1, 5, 10, 10, 5, 1] and
* corresponding upscaling are enough for 8-bit precision.
*
* For central filter here is used generic 9-tap filter with one of 3 different patterns
* combined with one of optional prefilters with fixed kernels. Kernel coefficients
* of the main filter are obtained from solution of least squares problem
* for Fourier transform of resulting kernel.
*/
#define STRIPE_WIDTH (1 << (C_ALIGN_ORDER - 1))
#define STRIPE_MASK (STRIPE_WIDTH - 1)
static int16_t zero_line[STRIPE_WIDTH];
static int16_t dither_line[2 * STRIPE_WIDTH] = {
#if STRIPE_WIDTH > 8
8, 40, 8, 40, 8, 40, 8, 40, 8, 40, 8, 40, 8, 40, 8, 40,
56, 24, 56, 24, 56, 24, 56, 24, 56, 24, 56, 24, 56, 24, 56, 24,
#else
8, 40, 8, 40, 8, 40, 8, 40,
56, 24, 56, 24, 56, 24, 56, 24,
#endif
};
inline static const int16_t *get_line(const int16_t *ptr, uintptr_t offs, uintptr_t size)
{
return offs < size ? ptr + offs : zero_line;
}
inline static void copy_line(int16_t *buf, const int16_t *ptr, uintptr_t offs, uintptr_t size)
{
ptr = get_line(ptr, offs, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
buf[k] = ptr[k];
}
/*
* Unpack/Pack Functions
*
* Convert between regular 8-bit bitmap and internal format.
* Internal image is stored as set of vertical stripes of size [STRIPE_WIDTH x height].
* Each pixel is represented as 16-bit integer in range of [0-0x4000].
*/
void ass_stripe_unpack_c(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride,
uintptr_t width, uintptr_t height)
{
for (uintptr_t y = 0; y < height; ++y) {
int16_t *ptr = dst;
for (uintptr_t x = 0; x < width; x += STRIPE_WIDTH) {
for (int k = 0; k < STRIPE_WIDTH; ++k)
ptr[k] = (uint16_t) (((src[x + k] << 7) | (src[x + k] >> 1)) + 1) >> 1;
//ptr[k] = (0x4000 * src[x + k] + 127) / 255;
ptr += STRIPE_WIDTH * height;
}
dst += STRIPE_WIDTH;
src += src_stride;
}
}
void ass_stripe_pack_c(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src,
uintptr_t width, uintptr_t height)
{
for (uintptr_t x = 0; x < width; x += STRIPE_WIDTH) {
uint8_t *ptr = dst;
for (uintptr_t y = 0; y < height; ++y) {
const int16_t *dither = dither_line + (y & 1) * STRIPE_WIDTH;
for (int k = 0; k < STRIPE_WIDTH; ++k)
ptr[k] = (uint16_t) (src[k] - (src[k] >> 8) + dither[k]) >> 6;
//ptr[k] = (255 * src[k] + 0x1FFF) / 0x4000;
ptr += dst_stride;
src += STRIPE_WIDTH;
}
dst += STRIPE_WIDTH;
}
uintptr_t left = dst_stride - ((width + STRIPE_MASK) & ~STRIPE_MASK);
for (uintptr_t y = 0; y < height; ++y) {
for (uintptr_t x = 0; x < left; ++x)
dst[x] = 0;
dst += dst_stride;
}
}
/*
* Contract Filters
*
* Contract image by factor 2 with kernel [1, 5, 10, 10, 5, 1].
*/
static inline int16_t shrink_func(int16_t p1p, int16_t p1n,
int16_t z0p, int16_t z0n,
int16_t n1p, int16_t n1n)
{
/*
return (1 * p1p + 5 * p1n + 10 * z0p + 10 * z0n + 5 * n1p + 1 * n1n + 16) >> 5;
*/
int32_t r = (p1p + p1n + n1p + n1n) >> 1;
r = (r + z0p + z0n) >> 1;
r = (r + p1n + n1p) >> 1;
return (r + z0p + z0n + 2) >> 2;
}
void ass_shrink_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_width = (src_width + 5) >> 1;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
int16_t buf[3 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr + 0 * STRIPE_WIDTH, src, offs + 0 * step, size);
copy_line(ptr + 1 * STRIPE_WIDTH, src, offs + 1 * step, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = shrink_func(ptr[2 * k - 4], ptr[2 * k - 3],
ptr[2 * k - 2], ptr[2 * k - 1],
ptr[2 * k + 0], ptr[2 * k + 1]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
offs += step;
}
}
void ass_shrink_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_height = (src_height + 5) >> 1;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; ++y) {
const int16_t *p1p = get_line(src, offs - 4 * STRIPE_WIDTH, step);
const int16_t *p1n = get_line(src, offs - 3 * STRIPE_WIDTH, step);
const int16_t *z0p = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *z0n = get_line(src, offs - 1 * STRIPE_WIDTH, step);
const int16_t *n1p = get_line(src, offs - 0 * STRIPE_WIDTH, step);
const int16_t *n1n = get_line(src, offs + 1 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = shrink_func(p1p[k], p1n[k], z0p[k], z0n[k], n1p[k], n1n[k]);
dst += STRIPE_WIDTH;
offs += 2 * STRIPE_WIDTH;
}
src += step;
}
}
/*
* Expand Filters
*
* Expand image by factor 2 with kernel [5, 10, 1], [1, 10, 5].
*/
static inline void expand_func(int16_t *rp, int16_t *rn,
int16_t p1, int16_t z0, int16_t n1)
{
/*
*rp = (5 * p1 + 10 * z0 + 1 * n1 + 8) >> 4;
*rn = (1 * p1 + 10 * z0 + 5 * n1 + 8) >> 4;
*/
uint16_t r = (uint16_t) (((uint16_t) (p1 + n1) >> 1) + z0) >> 1;
*rp = (uint16_t) (((uint16_t) (r + p1) >> 1) + z0 + 1) >> 1;
*rn = (uint16_t) (((uint16_t) (r + n1) >> 1) + z0 + 1) >> 1;
}
void ass_expand_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_width = 2 * src_width + 4;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
int16_t buf[2 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
for (uintptr_t x = STRIPE_WIDTH; x < dst_width; x += 2 * STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH / 2; ++k)
expand_func(&dst[2 * k], &dst[2 * k + 1],
ptr[k - 2], ptr[k - 1], ptr[k]);
int16_t *next = dst + step - STRIPE_WIDTH;
for (int k = STRIPE_WIDTH / 2; k < STRIPE_WIDTH; ++k)
expand_func(&next[2 * k], &next[2 * k + 1],
ptr[k - 2], ptr[k - 1], ptr[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
dst += step;
}
if ((dst_width - 1) & STRIPE_WIDTH)
return;
for (uintptr_t y = 0; y < src_height; ++y) {
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH / 2; ++k)
expand_func(&dst[2 * k], &dst[2 * k + 1],
ptr[k - 2], ptr[k - 1], ptr[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
}
void ass_expand_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_height = 2 * src_height + 4;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; y += 2) {
const int16_t *p1 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *z0 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
const int16_t *n1 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
expand_func(&dst[k], &dst[k + STRIPE_WIDTH],
p1[k], z0[k], n1[k]);
dst += 2 * STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
src += step;
}
}
/*
* First Supplementary Filters
*
* Perform 1D convolution with kernel [1, 2, 1].
*/
static inline int16_t pre_blur1_func(int16_t p1, int16_t z0, int16_t n1)
{
/*
return (1 * p1 + 2 * z0 + 1 * n1 + 2) >> 2;
*/
return (uint16_t) (((uint16_t) (p1 + n1) >> 1) + z0 + 1) >> 1;
}
void ass_pre_blur1_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_width = src_width + 2;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
int16_t buf[2 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = pre_blur1_func(ptr[k - 2], ptr[k - 1], ptr[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
}
}
void ass_pre_blur1_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_height = src_height + 2;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; ++y) {
const int16_t *p1 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *z0 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
const int16_t *n1 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = pre_blur1_func(p1[k], z0[k], n1[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
src += step;
}
}
/*
* Second Supplementary Filters
*
* Perform 1D convolution with kernel [1, 4, 6, 4, 1].
*/
static inline int16_t pre_blur2_func(int16_t p2, int16_t p1, int16_t z0,
int16_t n1, int16_t n2)
{
/*
return (1 * p2 + 4 * p1 + 6 * z0 + 4 * n1 + 1 * n2 + 8) >> 4;
*/
uint16_t r1 = ((uint16_t) (((uint16_t) (p2 + n2) >> 1) + z0) >> 1) + z0;
uint16_t r2 = p1 + n1;
uint16_t r = ((uint16_t) (r1 + r2) >> 1) | (0x8000 & r1 & r2);
return (uint16_t) (r + 1) >> 1;
}
void ass_pre_blur2_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_width = src_width + 4;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
int16_t buf[2 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = pre_blur2_func(ptr[k - 4], ptr[k - 3], ptr[k - 2], ptr[k - 1], ptr[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
}
}
void ass_pre_blur2_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_height = src_height + 4;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; ++y) {
const int16_t *p2 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
const int16_t *p1 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
const int16_t *z0 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *n1 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
const int16_t *n2 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = pre_blur2_func(p2[k], p1[k], z0[k], n1[k], n2[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
src += step;
}
}
/*
* Third Supplementary Filters
*
* Perform 1D convolution with kernel [1, 6, 15, 20, 15, 6, 1].
*/
static inline int16_t pre_blur3_func(int16_t p3, int16_t p2, int16_t p1, int16_t z0,
int16_t n1, int16_t n2, int16_t n3)
{
/*
return (1 * p3 + 6 * p2 + 15 * p1 + 20 * z0 + 15 * n1 + 6 * n2 + 1 * n3 + 32) >> 6;
*/
return (20 * (uint16_t) z0 +
15 * (uint16_t) (p1 + n1) +
6 * (uint16_t) (p2 + n2) +
1 * (uint16_t) (p3 + n3) + 32) >> 6;
}
void ass_pre_blur3_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_width = src_width + 6;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
int16_t buf[2 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = pre_blur3_func(ptr[k - 6], ptr[k - 5], ptr[k - 4], ptr[k - 3],
ptr[k - 2], ptr[k - 1], ptr[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
}
}
void ass_pre_blur3_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height)
{
uintptr_t dst_height = src_height + 6;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; ++y) {
const int16_t *p3 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
const int16_t *p2 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
const int16_t *p1 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
const int16_t *z0 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
const int16_t *n1 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *n2 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
const int16_t *n3 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = pre_blur3_func(p3[k], p2[k], p1[k], z0[k], n1[k], n2[k], n3[k]);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
src += step;
}
}
/*
* Main 9-tap Parametric Filters
*
* Perform 1D convolution with kernel
* [c3, c2, c1, c0, d, c0, c1, c2, c3] or
* [c3, 0, c2, c1, c0, d, c0, c1, c2, 0, c3] or
* [c3, 0, c2, 0, c1, c0, d, c0, c1, 0, c2, 0, c3] accordingly.
*
* cN = param[N], d = 1 - 2 * (c0 + c1 + c2 + c3).
*/
static inline int16_t blur_func(int16_t p4, int16_t p3, int16_t p2, int16_t p1, int16_t z0,
int16_t n1, int16_t n2, int16_t n3, int16_t n4, const int16_t c[])
{
p1 -= z0;
p2 -= z0;
p3 -= z0;
p4 -= z0;
n1 -= z0;
n2 -= z0;
n3 -= z0;
n4 -= z0;
return (((p1 + n1) * c[0] +
(p2 + n2) * c[1] +
(p3 + n3) * c[2] +
(p4 + n4) * c[3] +
0x8000) >> 16) + z0;
}
void ass_blur1234_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param)
{
uintptr_t dst_width = src_width + 8;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
int16_t buf[2 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = blur_func(ptr[k - 8], ptr[k - 7], ptr[k - 6], ptr[k - 5], ptr[k - 4],
ptr[k - 3], ptr[k - 2], ptr[k - 1], ptr[k - 0], param);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
}
}
void ass_blur1234_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param)
{
uintptr_t dst_height = src_height + 8;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; ++y) {
const int16_t *p4 = get_line(src, offs - 8 * STRIPE_WIDTH, step);
const int16_t *p3 = get_line(src, offs - 7 * STRIPE_WIDTH, step);
const int16_t *p2 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
const int16_t *p1 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
const int16_t *z0 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
const int16_t *n1 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
const int16_t *n2 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *n3 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
const int16_t *n4 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = blur_func(p4[k], p3[k], p2[k], p1[k], z0[k],
n1[k], n2[k], n3[k], n4[k], param);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
src += step;
}
}
void ass_blur1235_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param)
{
uintptr_t dst_width = src_width + 10;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
#if STRIPE_WIDTH < 10
int16_t buf[3 * STRIPE_WIDTH];
int16_t *ptr = buf + 2 * STRIPE_WIDTH;
#else
int16_t buf[2 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
#endif
for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
#if STRIPE_WIDTH < 10
copy_line(ptr - 2 * STRIPE_WIDTH, src, offs - 2 * step, size);
#endif
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = blur_func(ptr[k - 10], ptr[k - 8], ptr[k - 7], ptr[k - 6], ptr[k - 5],
ptr[k - 4], ptr[k - 3], ptr[k - 2], ptr[k - 0], param);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
}
}
void ass_blur1235_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param)
{
uintptr_t dst_height = src_height + 10;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; ++y) {
const int16_t *p4 = get_line(src, offs - 10 * STRIPE_WIDTH, step);
const int16_t *p3 = get_line(src, offs - 8 * STRIPE_WIDTH, step);
const int16_t *p2 = get_line(src, offs - 7 * STRIPE_WIDTH, step);
const int16_t *p1 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
const int16_t *z0 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
const int16_t *n1 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
const int16_t *n2 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
const int16_t *n3 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *n4 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = blur_func(p4[k], p3[k], p2[k], p1[k], z0[k],
n1[k], n2[k], n3[k], n4[k], param);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
src += step;
}
}
void ass_blur1246_horz_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param)
{
uintptr_t dst_width = src_width + 12;
uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
uintptr_t step = STRIPE_WIDTH * src_height;
uintptr_t offs = 0;
#if STRIPE_WIDTH < 12
int16_t buf[3 * STRIPE_WIDTH];
int16_t *ptr = buf + 2 * STRIPE_WIDTH;
#else
int16_t buf[2 * STRIPE_WIDTH];
int16_t *ptr = buf + STRIPE_WIDTH;
#endif
for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
for (uintptr_t y = 0; y < src_height; ++y) {
#if STRIPE_WIDTH < 12
copy_line(ptr - 2 * STRIPE_WIDTH, src, offs - 2 * step, size);
#endif
copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = blur_func(ptr[k - 12], ptr[k - 10], ptr[k - 8], ptr[k - 7], ptr[k - 6],
ptr[k - 5], ptr[k - 4], ptr[k - 2], ptr[k - 0], param);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
}
}
void ass_blur1246_vert_c(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param)
{
uintptr_t dst_height = src_height + 12;
uintptr_t step = STRIPE_WIDTH * src_height;
for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
uintptr_t offs = 0;
for (uintptr_t y = 0; y < dst_height; ++y) {
const int16_t *p4 = get_line(src, offs - 12 * STRIPE_WIDTH, step);
const int16_t *p3 = get_line(src, offs - 10 * STRIPE_WIDTH, step);
const int16_t *p2 = get_line(src, offs - 8 * STRIPE_WIDTH, step);
const int16_t *p1 = get_line(src, offs - 7 * STRIPE_WIDTH, step);
const int16_t *z0 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
const int16_t *n1 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
const int16_t *n2 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
const int16_t *n3 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
const int16_t *n4 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
for (int k = 0; k < STRIPE_WIDTH; ++k)
dst[k] = blur_func(p4[k], p3[k], p2[k], p1[k], z0[k],
n1[k], n2[k], n3[k], n4[k], param);
dst += STRIPE_WIDTH;
offs += STRIPE_WIDTH;
}
src += step;
}
}
static void calc_gauss(double *res, int n, double r2)
{
double alpha = 0.5 / r2;
double mul = exp(-alpha), mul2 = mul * mul;
double cur = sqrt(alpha / M_PI);
res[0] = cur;
cur *= mul;
res[1] = cur;
for (int i = 2; i <= n; ++i) {
mul *= mul2;
cur *= mul;
res[i] = cur;
}
}
static void coeff_blur121(double *coeff, int n)
{
double prev = coeff[1];
for (int i = 0; i <= n; ++i) {
double res = (prev + 2 * coeff[i] + coeff[i + 1]) / 4;
prev = coeff[i];
coeff[i] = res;
}
}
static void coeff_filter(double *coeff, int n, const double kernel[4])
{
double prev1 = coeff[1], prev2 = coeff[2], prev3 = coeff[3];
for (int i = 0; i <= n; ++i) {
double res = coeff[i + 0] * kernel[0] +
(prev1 + coeff[i + 1]) * kernel[1] +
(prev2 + coeff[i + 2]) * kernel[2] +
(prev3 + coeff[i + 3]) * kernel[3];
prev3 = prev2;
prev2 = prev1;
prev1 = coeff[i];
coeff[i] = res;
}
}
static void calc_matrix(double mat[4][4], const double *mat_freq, const int *index)
{
for (int i = 0; i < 4; ++i) {
mat[i][i] = mat_freq[2 * index[i]] + 3 * mat_freq[0] - 4 * mat_freq[index[i]];
for (int j = i + 1; j < 4; ++j)
mat[i][j] = mat[j][i] =
mat_freq[index[i] + index[j]] + mat_freq[index[j] - index[i]] +
2 * (mat_freq[0] - mat_freq[index[i]] - mat_freq[index[j]]);
}
// invert transpose
for (int k = 0; k < 4; ++k) {
int ip = k, jp = k; // pivot
double z = 1 / mat[ip][jp];
mat[ip][jp] = 1;
for (int i = 0; i < 4; ++i) {
if (i == ip)
continue;
double mul = mat[i][jp] * z;
mat[i][jp] = 0;
for (int j = 0; j < 4; ++j)
mat[i][j] -= mat[ip][j] * mul;
}
for (int j = 0; j < 4; ++j)
mat[ip][j] *= z;
}
}
/**
* \brief Solve least squares problem for kernel of the main filter
* \param mu out: output coefficients
* \param index in: filter tap positions
* \param prefilter in: supplementary filter type
* \param r2 in: desired standard deviation squared
* \param mul in: scale multiplier
*/
static void calc_coeff(double mu[4], const int index[4], int prefilter, double r2, double mul)
{
double mul2 = mul * mul, mul3 = mul2 * mul;
double kernel[] = {
(5204 + 2520 * mul + 1092 * mul2 + 3280 * mul3) / 12096,
(2943 - 210 * mul - 273 * mul2 - 2460 * mul3) / 12096,
( 486 - 924 * mul - 546 * mul2 + 984 * mul3) / 12096,
( 17 - 126 * mul + 273 * mul2 - 164 * mul3) / 12096,
};
double mat_freq[14];
memcpy(mat_freq, kernel, sizeof(kernel));
memset(mat_freq + 4, 0, sizeof(mat_freq) - sizeof(kernel));
int n = 6;
coeff_filter(mat_freq, n, kernel);
for (int k = 0; k < 2 * prefilter; ++k)
coeff_blur121(mat_freq, ++n);
double vec_freq[13];
n = index[3] + prefilter + 3;
calc_gauss(vec_freq, n, r2);
memset(vec_freq + n + 1, 0, sizeof(vec_freq) - (n + 1) * sizeof(vec_freq[0]));
n -= 3;
coeff_filter(vec_freq, n, kernel);
for (int k = 0; k < prefilter; ++k)
coeff_blur121(vec_freq, --n);
double mat[4][4];
calc_matrix(mat, mat_freq, index);
double vec[4];
for (int i = 0; i < 4; ++i)
vec[i] = mat_freq[0] - mat_freq[index[i]] - vec_freq[0] + vec_freq[index[i]];
for (int i = 0; i < 4; ++i) {
double res = 0;
for (int j = 0; j < 4; ++j)
res += mat[i][j] * vec[j];
mu[i] = FFMAX(0, res);
}
}
typedef struct {
int level, prefilter, filter;
int16_t coeff[4];
} BlurMethod;
static void find_best_method(BlurMethod *blur, double r2)
{
static const int index[][4] = {
{ 1, 2, 3, 4 },
{ 1, 2, 3, 5 },
{ 1, 2, 4, 6 },
};
double mu[5];
if (r2 < 1.9) {
blur->level = blur->prefilter = blur->filter = 0;
if (r2 < 0.5) {
mu[2] = 0.085 * r2 * r2 * r2;
mu[1] = 0.5 * r2 - 4 * mu[2];
mu[3] = mu[4] = 0;
} else {
calc_gauss(mu, 4, r2);
}
} else {
double mul = 1;
if (r2 < 6.693) {
blur->level = 0;
if (r2 < 2.8)
blur->prefilter = 1;
else if (r2 < 4.4)
blur->prefilter = 2;
else
blur->prefilter = 3;
blur->filter = blur->prefilter - 1;
} else {
frexp((r2 + 0.7) / 26.5, &blur->level);
blur->level = (blur->level + 3) >> 1;
mul = pow(0.25, blur->level);
r2 *= mul;
if (r2 < 3.15 - 1.5 * mul)
blur->prefilter = 0;
else if (r2 < 5.3 - 5.2 * mul)
blur->prefilter = 1;
else
blur->prefilter = 2;
blur->filter = blur->prefilter;
}
calc_coeff(mu + 1, index[blur->filter], blur->prefilter, r2, mul);
}
for (int i = 1; i <= 4; ++i)
blur->coeff[i - 1] = (int) (0x10000 * mu[i] + 0.5);
}
/**
* \brief Perform approximate gaussian blur
* \param r2 in: desired standard deviation squared
*/
bool ass_gaussian_blur(const BitmapEngine *engine, Bitmap *bm, double r2)
{
BlurMethod blur;
find_best_method(&blur, r2);
int w = bm->w, h = bm->h;
int offset = ((2 * (blur.prefilter + blur.filter) + 17) << blur.level) - 5;
int end_w = ((w + offset) & ~((1 << blur.level) - 1)) - 4;
int end_h = ((h + offset) & ~((1 << blur.level) - 1)) - 4;
const int stripe_width = 1 << (engine->align_order - 1);
int size = end_h * ((end_w + stripe_width - 1) & ~(stripe_width - 1));
int16_t *tmp = ass_aligned_alloc(2 * stripe_width, 4 * size, false);
if (!tmp)
return false;
engine->stripe_unpack(tmp, bm->buffer, bm->stride, w, h);
int16_t *buf[2] = {tmp, tmp + size};
int index = 0;
for (int i = 0; i < blur.level; ++i) {
engine->shrink_vert(buf[index ^ 1], buf[index], w, h);
h = (h + 5) >> 1;
index ^= 1;
}
for (int i = 0; i < blur.level; ++i) {
engine->shrink_horz(buf[index ^ 1], buf[index], w, h);
w = (w + 5) >> 1;
index ^= 1;
}
if (blur.prefilter) {
engine->pre_blur_horz[blur.prefilter - 1](buf[index ^ 1], buf[index], w, h);
w += 2 * blur.prefilter;
index ^= 1;
}
engine->main_blur_horz[blur.filter](buf[index ^ 1], buf[index], w, h, blur.coeff);
w += 2 * blur.filter + 8;
index ^= 1;
for (int i = 0; i < blur.level; ++i) {
engine->expand_horz(buf[index ^ 1], buf[index], w, h);
w = 2 * w + 4;
index ^= 1;
}
if (blur.prefilter) {
engine->pre_blur_vert[blur.prefilter - 1](buf[index ^ 1], buf[index], w, h);
h += 2 * blur.prefilter;
index ^= 1;
}
engine->main_blur_vert[blur.filter](buf[index ^ 1], buf[index], w, h, blur.coeff);
h += 2 * blur.filter + 8;
index ^= 1;
for (int i = 0; i < blur.level; ++i) {
engine->expand_vert(buf[index ^ 1], buf[index], w, h);
h = 2 * h + 4;
index ^= 1;
}
assert(w == end_w && h == end_h);
if (!realloc_bitmap(engine, bm, w, h)) {
ass_aligned_free(tmp);
return false;
}
offset = ((blur.prefilter + blur.filter + 8) << blur.level) - 4;
bm->left -= offset;
bm->top -= offset;
engine->stripe_pack(bm->buffer, bm->stride, buf[index], w, h);
ass_aligned_free(tmp);
return true;
}

580
libass/ass_cache.c Normal file
View File

@ -0,0 +1,580 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <inttypes.h>
#include <ft2build.h>
#include FT_OUTLINE_H
#include <assert.h>
#include "ass_utils.h"
#include "ass_font.h"
#include "ass_outline.h"
#include "ass_cache.h"
// type-specific functions
// create hash/compare functions for bitmap, outline and composite cache
#define CREATE_HASH_FUNCTIONS
#include "ass_cache_template.h"
#define CREATE_COMPARISON_FUNCTIONS
#include "ass_cache_template.h"
// font cache
static unsigned font_hash(void *buf, size_t len)
{
ASS_FontDesc *desc = buf;
unsigned hval;
hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval);
hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval);
return hval;
}
static unsigned font_compare(void *key1, void *key2, size_t key_size)
{
ASS_FontDesc *a = key1;
ASS_FontDesc *b = key2;
if (strcmp(a->family, b->family) != 0)
return 0;
if (a->bold != b->bold)
return 0;
if (a->italic != b->italic)
return 0;
if (a->vertical != b->vertical)
return 0;
return 1;
}
static bool font_key_move(void *dst, void *src, size_t key_size)
{
ASS_FontDesc *k = src;
if (dst)
memcpy(dst, src, key_size);
else
free(k->family);
return true;
}
static void font_destruct(void *key, void *value)
{
ass_font_clear(value);
}
const CacheDesc font_cache_desc = {
.hash_func = font_hash,
.compare_func = font_compare,
.key_move_func = font_key_move,
.destruct_func = font_destruct,
.key_size = sizeof(ASS_FontDesc),
.value_size = sizeof(ASS_Font)
};
// bitmap cache
static unsigned bitmap_hash(void *key, size_t key_size)
{
BitmapHashKey *k = key;
switch (k->type) {
case BITMAP_OUTLINE: return outline_bitmap_hash(&k->u, key_size);
case BITMAP_CLIP: return clip_bitmap_hash(&k->u, key_size);
default: return 0;
}
}
static unsigned bitmap_compare(void *a, void *b, size_t key_size)
{
BitmapHashKey *ak = a;
BitmapHashKey *bk = b;
if (ak->type != bk->type) return 0;
switch (ak->type) {
case BITMAP_OUTLINE: return outline_bitmap_compare(&ak->u, &bk->u, key_size);
case BITMAP_CLIP: return clip_bitmap_compare(&ak->u, &bk->u, key_size);
default: return 0;
}
}
static bool bitmap_key_move(void *dst, void *src, size_t key_size)
{
BitmapHashKey *d = dst, *s = src;
if (!dst) {
if (s->type == BITMAP_OUTLINE)
ass_cache_dec_ref(s->u.outline.outline);
return true;
}
memcpy(dst, src, key_size);
if (s->type != BITMAP_CLIP)
return true;
d->u.clip.text = strdup(s->u.clip.text);
return d->u.clip.text;
}
static void bitmap_destruct(void *key, void *value)
{
BitmapHashValue *v = value;
BitmapHashKey *k = key;
if (v->bm)
ass_free_bitmap(v->bm);
if (v->bm_o)
ass_free_bitmap(v->bm_o);
switch (k->type) {
case BITMAP_OUTLINE: ass_cache_dec_ref(k->u.outline.outline); break;
case BITMAP_CLIP: free(k->u.clip.text); break;
}
}
const CacheDesc bitmap_cache_desc = {
.hash_func = bitmap_hash,
.compare_func = bitmap_compare,
.key_move_func = bitmap_key_move,
.destruct_func = bitmap_destruct,
.key_size = sizeof(BitmapHashKey),
.value_size = sizeof(BitmapHashValue)
};
// composite cache
static unsigned composite_hash(void *key, size_t key_size)
{
CompositeHashKey *k = key;
unsigned hval = filter_hash(&k->filter, key_size);
for (size_t i = 0; i < k->bitmap_count; i++) {
hval = fnv_32a_buf(&k->bitmaps[i].image, sizeof(k->bitmaps[i].image), hval);
hval = fnv_32a_buf(&k->bitmaps[i].x, sizeof(k->bitmaps[i].x), hval);
hval = fnv_32a_buf(&k->bitmaps[i].y, sizeof(k->bitmaps[i].y), hval);
}
return hval;
}
static unsigned composite_compare(void *a, void *b, size_t key_size)
{
CompositeHashKey *ak = a;
CompositeHashKey *bk = b;
if (ak->bitmap_count != bk->bitmap_count)
return 0;
for (size_t i = 0; i < ak->bitmap_count; i++) {
if (ak->bitmaps[i].image != bk->bitmaps[i].image ||
ak->bitmaps[i].x != bk->bitmaps[i].x ||
ak->bitmaps[i].y != bk->bitmaps[i].y)
return 0;
}
return filter_compare(&ak->filter, &bk->filter, key_size);
}
static bool composite_key_move(void *dst, void *src, size_t key_size)
{
if (dst) {
memcpy(dst, src, key_size);
return true;
}
CompositeHashKey *k = src;
for (size_t i = 0; i < k->bitmap_count; i++)
ass_cache_dec_ref(k->bitmaps[i].image);
free(k->bitmaps);
return true;
}
static void composite_destruct(void *key, void *value)
{
CompositeHashValue *v = value;
CompositeHashKey *k = key;
if (v->bm)
ass_free_bitmap(v->bm);
if (v->bm_o)
ass_free_bitmap(v->bm_o);
if (v->bm_s)
ass_free_bitmap(v->bm_s);
for (size_t i = 0; i < k->bitmap_count; i++)
ass_cache_dec_ref(k->bitmaps[i].image);
free(k->bitmaps);
}
const CacheDesc composite_cache_desc = {
.hash_func = composite_hash,
.compare_func = composite_compare,
.key_move_func = composite_key_move,
.destruct_func = composite_destruct,
.key_size = sizeof(CompositeHashKey),
.value_size = sizeof(CompositeHashValue)
};
// outline cache
static unsigned outline_hash(void *key, size_t key_size)
{
OutlineHashKey *k = key;
switch (k->type) {
case OUTLINE_GLYPH: return glyph_hash(&k->u, key_size);
case OUTLINE_DRAWING: return drawing_hash(&k->u, key_size);
default: return 0;
}
}
static unsigned outline_compare(void *a, void *b, size_t key_size)
{
OutlineHashKey *ak = a;
OutlineHashKey *bk = b;
if (ak->type != bk->type) return 0;
switch (ak->type) {
case OUTLINE_GLYPH: return glyph_compare(&ak->u, &bk->u, key_size);
case OUTLINE_DRAWING: return drawing_compare(&ak->u, &bk->u, key_size);
default: return 0;
}
}
static bool outline_key_move(void *dst, void *src, size_t key_size)
{
OutlineHashKey *d = dst, *s = src;
if (!dst) {
if (s->type == OUTLINE_GLYPH)
ass_cache_dec_ref(s->u.glyph.font);
return true;
}
memcpy(dst, src, key_size);
if (s->type != OUTLINE_DRAWING)
return true;
d->u.drawing.text = strdup(s->u.drawing.text);
return d->u.drawing.text;
}
static void outline_destruct(void *key, void *value)
{
OutlineHashValue *v = value;
OutlineHashKey *k = key;
outline_free(&v->outline);
outline_free(&v->border[0]);
outline_free(&v->border[1]);
switch (k->type) {
case OUTLINE_GLYPH: ass_cache_dec_ref(k->u.glyph.font); break;
case OUTLINE_DRAWING: free(k->u.drawing.text); break;
}
}
const CacheDesc outline_cache_desc = {
.hash_func = outline_hash,
.compare_func = outline_compare,
.key_move_func = outline_key_move,
.destruct_func = outline_destruct,
.key_size = sizeof(OutlineHashKey),
.value_size = sizeof(OutlineHashValue)
};
// glyph metric cache
static bool glyph_metrics_key_move(void *dst, void *src, size_t key_size)
{
if (!dst)
return true;
memcpy(dst, src, key_size);
GlyphMetricsHashKey *k = src;
ass_cache_inc_ref(k->font);
return true;
}
static void glyph_metrics_destruct(void *key, void *value)
{
GlyphMetricsHashKey *k = key;
ass_cache_dec_ref(k->font);
}
const CacheDesc glyph_metrics_cache_desc = {
.hash_func = glyph_metrics_hash,
.compare_func = glyph_metrics_compare,
.key_move_func = glyph_metrics_key_move,
.destruct_func = glyph_metrics_destruct,
.key_size = sizeof(GlyphMetricsHashKey),
.value_size = sizeof(GlyphMetricsHashValue)
};
// Cache data
typedef struct cache_item {
Cache *cache;
const CacheDesc *desc;
struct cache_item *next, **prev;
struct cache_item *queue_next, **queue_prev;
size_t size, ref_count;
} CacheItem;
struct cache {
unsigned buckets;
CacheItem **map;
CacheItem *queue_first, **queue_last;
const CacheDesc *desc;
size_t cache_size;
unsigned hits;
unsigned misses;
unsigned items;
};
#define CACHE_ALIGN 8
#define CACHE_ITEM_SIZE ((sizeof(CacheItem) + (CACHE_ALIGN - 1)) & ~(CACHE_ALIGN - 1))
static inline size_t align_cache(size_t size)
{
return (size + (CACHE_ALIGN - 1)) & ~(CACHE_ALIGN - 1);
}
static inline CacheItem *value_to_item(void *value)
{
return (CacheItem *) ((char *) value - CACHE_ITEM_SIZE);
}
// Create a cache with type-specific hash/compare/destruct/size functions
Cache *ass_cache_create(const CacheDesc *desc)
{
Cache *cache = calloc(1, sizeof(*cache));
if (!cache)
return NULL;
cache->buckets = 0xFFFF;
cache->queue_last = &cache->queue_first;
cache->desc = desc;
cache->map = calloc(cache->buckets, sizeof(CacheItem *));
if (!cache->map) {
free(cache);
return NULL;
}
return cache;
}
bool ass_cache_get(Cache *cache, void *key, void *value_ptr)
{
char **value = (char **) value_ptr;
const CacheDesc *desc = cache->desc;
size_t key_offs = CACHE_ITEM_SIZE + align_cache(desc->value_size);
unsigned bucket = desc->hash_func(key, desc->key_size) % cache->buckets;
CacheItem *item = cache->map[bucket];
while (item) {
if (desc->compare_func(key, (char *) item + key_offs, desc->key_size)) {
assert(item->size);
if (!item->queue_prev || item->queue_next) {
if (item->queue_prev) {
item->queue_next->queue_prev = item->queue_prev;
*item->queue_prev = item->queue_next;
} else
item->ref_count++;
*cache->queue_last = item;
item->queue_prev = cache->queue_last;
cache->queue_last = &item->queue_next;
item->queue_next = NULL;
}
cache->hits++;
desc->key_move_func(NULL, key, desc->key_size);
*value = (char *) item + CACHE_ITEM_SIZE;
item->ref_count++;
return true;
}
item = item->next;
}
cache->misses++;
item = malloc(key_offs + desc->key_size);
if (!item) {
desc->key_move_func(NULL, key, desc->key_size);
*value = NULL;
return false;
}
item->size = 0;
item->cache = cache;
item->desc = desc;
if (!desc->key_move_func((char *) item + key_offs, key, desc->key_size)) {
free(item);
*value = NULL;
return false;
}
*value = (char *) item + CACHE_ITEM_SIZE;
CacheItem **bucketptr = &cache->map[bucket];
if (*bucketptr)
(*bucketptr)->prev = &item->next;
item->prev = bucketptr;
item->next = *bucketptr;
*bucketptr = item;
item->queue_prev = NULL;
item->queue_next = NULL;
item->ref_count = 1;
return false;
}
void *ass_cache_key(void *value)
{
CacheItem *item = value_to_item(value);
return (char *) value + align_cache(item->desc->value_size);
}
void ass_cache_commit(void *value, size_t item_size)
{
CacheItem *item = value_to_item(value);
assert(!item->size && item_size);
item->size = item_size;
Cache *cache = item->cache;
cache->cache_size += item_size;
cache->items++;
*cache->queue_last = item;
item->queue_prev = cache->queue_last;
cache->queue_last = &item->queue_next;
item->ref_count++;
}
static inline void destroy_item(const CacheDesc *desc, CacheItem *item)
{
assert(item->desc == desc);
char *value = (char *) item + CACHE_ITEM_SIZE;
desc->destruct_func(value + align_cache(desc->value_size), value);
free(item);
}
void ass_cache_inc_ref(void *value)
{
if (!value)
return;
CacheItem *item = value_to_item(value);
assert(item->size && item->ref_count);
item->ref_count++;
}
void ass_cache_dec_ref(void *value)
{
if (!value)
return;
CacheItem *item = value_to_item(value);
assert(item->size && item->ref_count);
if (--item->ref_count)
return;
Cache *cache = item->cache;
if (cache) {
if (item->next)
item->next->prev = item->prev;
*item->prev = item->next;
cache->items--;
cache->cache_size -= item->size;
}
destroy_item(item->desc, item);
}
void ass_cache_cut(Cache *cache, size_t max_size)
{
if (cache->cache_size <= max_size)
return;
do {
CacheItem *item = cache->queue_first;
if (!item)
break;
assert(item->size);
cache->queue_first = item->queue_next;
if (--item->ref_count) {
item->queue_prev = NULL;
continue;
}
if (item->next)
item->next->prev = item->prev;
*item->prev = item->next;
cache->items--;
cache->cache_size -= item->size;
destroy_item(cache->desc, item);
} while (cache->cache_size > max_size);
if (cache->queue_first)
cache->queue_first->queue_prev = &cache->queue_first;
else
cache->queue_last = &cache->queue_first;
}
void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits,
unsigned *misses, unsigned *count)
{
if (size)
*size = cache->cache_size;
if (hits)
*hits = cache->hits;
if (misses)
*misses = cache->misses;
if (count)
*count = cache->items;
}
void ass_cache_empty(Cache *cache)
{
for (int i = 0; i < cache->buckets; i++) {
CacheItem *item = cache->map[i];
while (item) {
assert(item->size);
CacheItem *next = item->next;
if (item->queue_prev)
item->ref_count--;
if (item->ref_count)
item->cache = NULL;
else
destroy_item(cache->desc, item);
item = next;
}
cache->map[i] = NULL;
}
cache->queue_first = NULL;
cache->queue_last = &cache->queue_first;
cache->items = cache->hits = cache->misses = cache->cache_size = 0;
}
void ass_cache_done(Cache *cache)
{
ass_cache_empty(cache);
free(cache->map);
free(cache);
}
// Type-specific creation function
Cache *ass_font_cache_create(void)
{
return ass_cache_create(&font_cache_desc);
}
Cache *ass_outline_cache_create(void)
{
return ass_cache_create(&outline_cache_desc);
}
Cache *ass_glyph_metrics_cache_create(void)
{
return ass_cache_create(&glyph_metrics_cache_desc);
}
Cache *ass_bitmap_cache_create(void)
{
return ass_cache_create(&bitmap_cache_desc);
}
Cache *ass_composite_cache_create(void)
{
return ass_cache_create(&composite_cache_desc);
}

136
libass/ass_cache.h Normal file
View File

@ -0,0 +1,136 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_CACHE_H
#define LIBASS_CACHE_H
#include "ass.h"
#include "ass_font.h"
#include "ass_outline.h"
#include "ass_bitmap.h"
typedef struct cache Cache;
// cache values
typedef struct {
bool valid;
Bitmap *bm; // the actual bitmaps
Bitmap *bm_o;
} BitmapHashValue;
typedef struct {
Bitmap *bm;
Bitmap *bm_o;
Bitmap *bm_s;
} CompositeHashValue;
typedef struct {
bool valid;
ASS_Outline outline;
ASS_Outline border[2];
ASS_Rect bbox_scaled; // bbox after scaling, but before rotation
ASS_Vector advance; // 26.6, advance distance to the next outline in line
int asc, desc; // ascender/descender
} OutlineHashValue;
typedef struct {
FT_Glyph_Metrics metrics;
} GlyphMetricsHashValue;
// Create definitions for bitmap, outline and composite hash keys
#define CREATE_STRUCT_DEFINITIONS
#include "ass_cache_template.h"
// Type-specific function pointers
typedef unsigned(*HashFunction)(void *key, size_t key_size);
typedef unsigned(*HashCompare)(void *a, void *b, size_t key_size);
typedef bool(*CacheKeyMove)(void *dst, void *src, size_t key_size);
typedef void(*CacheItemDestructor)(void *key, void *value);
// cache hash keys
typedef struct outline_hash_key {
enum {
OUTLINE_GLYPH,
OUTLINE_DRAWING,
} type;
union {
GlyphHashKey glyph;
DrawingHashKey drawing;
} u;
} OutlineHashKey;
typedef struct bitmap_hash_key {
enum {
BITMAP_OUTLINE,
BITMAP_CLIP,
} type;
union {
OutlineBitmapHashKey outline;
ClipMaskHashKey clip;
} u;
} BitmapHashKey;
typedef struct {
BitmapHashValue *image;
int x, y;
} BitmapRef;
enum {
FILTER_BORDER_STYLE_3 = 1,
FILTER_NONZERO_BORDER = 2,
FILTER_NONZERO_SHADOW = 4,
FILTER_DRAW_SHADOW = 8, // VSFilter compatibility
};
typedef struct {
FilterDesc filter;
size_t bitmap_count;
BitmapRef *bitmaps;
} CompositeHashKey;
typedef struct
{
HashFunction hash_func;
HashCompare compare_func;
CacheKeyMove key_move_func;
CacheItemDestructor destruct_func;
size_t key_size;
size_t value_size;
} CacheDesc;
Cache *ass_cache_create(const CacheDesc *desc);
bool ass_cache_get(Cache *cache, void *key, void *value_ptr);
void *ass_cache_key(void *value);
void ass_cache_commit(void *value, size_t item_size);
void ass_cache_inc_ref(void *value);
void ass_cache_dec_ref(void *value);
void ass_cache_cut(Cache *cache, size_t max_size);
void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits,
unsigned *misses, unsigned *count);
void ass_cache_empty(Cache *cache);
void ass_cache_done(Cache *cache);
Cache *ass_font_cache_create(void);
Cache *ass_outline_cache_create(void);
Cache *ass_glyph_metrics_cache_create(void);
Cache *ass_bitmap_cache_create(void);
Cache *ass_composite_cache_create(void);
#endif /* LIBASS_CACHE_H */

134
libass/ass_cache_template.h Normal file
View File

@ -0,0 +1,134 @@
#ifdef CREATE_STRUCT_DEFINITIONS
#undef CREATE_STRUCT_DEFINITIONS
#define START(funcname, structname) \
typedef struct structname {
#define GENERIC(type, member) \
type member;
#define STRING(member) \
char *member;
#define VECTOR(member) \
ASS_Vector member;
#define BITMAPHASHKEY(member) \
BitmapHashKey member;
#define END(typedefnamename) \
} typedefnamename;
#elif defined(CREATE_COMPARISON_FUNCTIONS)
#undef CREATE_COMPARISON_FUNCTIONS
#define START(funcname, structname) \
static unsigned funcname##_compare(void *key1, void *key2, size_t key_size) \
{ \
struct structname *a = key1; \
struct structname *b = key2; \
return // conditions follow
#define GENERIC(type, member) \
a->member == b->member &&
#define STRING(member) \
strcmp(a->member, b->member) == 0 &&
#define VECTOR(member) \
a->member.x == b->member.x && a->member.y == b->member.y &&
#define BITMAPHASHKEY(member) \
bitmap_compare(&a->member, &b->member, sizeof(a->member)) &&
#define END(typedefname) \
1; \
}
#elif defined(CREATE_HASH_FUNCTIONS)
#undef CREATE_HASH_FUNCTIONS
#define START(funcname, structname) \
static unsigned funcname##_hash(void *buf, size_t len) \
{ \
struct structname *p = buf; \
unsigned hval = FNV1_32A_INIT;
#define GENERIC(type, member) \
hval = fnv_32a_buf(&p->member, sizeof(p->member), hval);
#define STRING(member) \
hval = fnv_32a_str(p->member, hval);
#define VECTOR(member) GENERIC(, member.x); GENERIC(, member.y);
#define BITMAPHASHKEY(member) { \
unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \
hval = fnv_32a_buf(&temp, sizeof(temp), hval); \
}
#define END(typedefname) \
return hval; \
}
#else
#error missing defines
#endif
// describes an outline bitmap
START(outline_bitmap, outline_bitmap_hash_key)
GENERIC(OutlineHashValue *, outline)
GENERIC(int, frx) // signed 10.22
GENERIC(int, fry) // signed 10.22
GENERIC(int, frz) // signed 10.22
GENERIC(int, fax) // signed 16.16
GENERIC(int, fay) // signed 16.16
// shift vector that was added to glyph before applying rotation
// = 0, if frx = fry = frx = 0
// = (glyph base point) - (rotation origin), otherwise
GENERIC(int, shift_x)
GENERIC(int, shift_y)
VECTOR(advance) // subpixel shift vector
END(OutlineBitmapHashKey)
// describe a clip mask bitmap
START(clip_bitmap, clip_bitmap_hash_key)
STRING(text)
END(ClipMaskHashKey)
// describes an outline glyph
START(glyph, glyph_hash_key)
GENERIC(ASS_Font *, font)
GENERIC(double, size) // font size
GENERIC(int, face_index)
GENERIC(int, glyph_index)
GENERIC(int, bold)
GENERIC(int, italic)
GENERIC(unsigned, scale_x) // 16.16
GENERIC(unsigned, scale_y) // 16.16
VECTOR(outline) // border width, 26.6
GENERIC(unsigned, flags) // glyph decoration flags
GENERIC(unsigned, border_style)
GENERIC(int, hspacing) // 16.16
END(GlyphHashKey)
START(glyph_metrics, glyph_metrics_hash_key)
GENERIC(ASS_Font *, font)
GENERIC(double, size)
GENERIC(int, face_index)
GENERIC(int, glyph_index)
GENERIC(unsigned, scale_x)
GENERIC(unsigned, scale_y)
END(GlyphMetricsHashKey)
// describes an outline drawing
START(drawing, drawing_hash_key)
GENERIC(unsigned, scale_x)
GENERIC(unsigned, scale_y)
GENERIC(int, pbo)
VECTOR(outline)
GENERIC(unsigned, border_style)
GENERIC(int, hspacing)
GENERIC(int, scale)
GENERIC(unsigned, hash)
STRING(text)
END(DrawingHashKey)
// describes post-combining effects
START(filter, filter_desc)
GENERIC(int, flags)
GENERIC(int, be)
GENERIC(double, blur)
VECTOR(shadow)
END(FilterDesc)
#undef START
#undef GENERIC
#undef STRING
#undef VECTOR
#undef BITMAPHASHKEY
#undef END

29
libass/ass_compat.h Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2015 Oleg Oshmyan <chortos@inbox.lv>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_COMPAT_H
#define LIBASS_COMPAT_H
#ifdef _MSC_VER
#define _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#define inline __inline
#endif
#endif /* LIBASS_COMPAT_H */

324
libass/ass_coretext.c Normal file
View File

@ -0,0 +1,324 @@
/*
* Copyright (C) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <CoreFoundation/CoreFoundation.h>
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include <CoreText/CoreText.h>
#else
#include <ApplicationServices/ApplicationServices.h>
#endif
#include "ass_coretext.h"
#define SAFE_CFRelease(x) do { if (x) CFRelease(x); } while(0)
static const ASS_FontMapping font_substitutions[] = {
{"sans-serif", "Helvetica"},
{"serif", "Times"},
{"monospace", "Courier"}
};
static char *cfstr2buf(CFStringRef string)
{
if (!string)
return NULL;
const int encoding = kCFStringEncodingUTF8;
const char *buf_ptr = CFStringGetCStringPtr(string, encoding);
if (buf_ptr) {
return strdup(buf_ptr);
} else {
size_t len = CFStringGetLength(string);
CFIndex buf_len = CFStringGetMaximumSizeForEncoding(len, encoding);
char *buf = malloc(buf_len);
CFStringGetCString(string, buf, buf_len, encoding);
return buf;
}
}
static void destroy_font(void *priv)
{
CTFontDescriptorRef fontd = priv;
SAFE_CFRelease(fontd);
}
static bool check_postscript(void *priv)
{
CTFontDescriptorRef fontd = priv;
CFNumberRef cfformat =
CTFontDescriptorCopyAttribute(fontd, kCTFontFormatAttribute);
int format;
if (!CFNumberGetValue(cfformat, kCFNumberIntType, &format))
return false;
SAFE_CFRelease(cfformat);
return format == kCTFontFormatOpenTypePostScript ||
format == kCTFontFormatPostScript;
}
static bool check_glyph(void *priv, uint32_t code)
{
if (code == 0)
return true;
CTFontDescriptorRef fontd = priv;
CFCharacterSetRef set =
CTFontDescriptorCopyAttribute(fontd, kCTFontCharacterSetAttribute);
if (!set)
return true;
bool result = CFCharacterSetIsLongCharacterMember(set, code);
SAFE_CFRelease(set);
return result;
}
static char *get_font_file(CTFontDescriptorRef fontd)
{
CFURLRef url = CTFontDescriptorCopyAttribute(fontd, kCTFontURLAttribute);
CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
char *buffer = cfstr2buf(path);
SAFE_CFRelease(path);
SAFE_CFRelease(url);
return buffer;
}
static void get_name(CTFontDescriptorRef fontd, CFStringRef attr,
char **array, int *idx)
{
CFStringRef name = CTFontDescriptorCopyAttribute(fontd, attr);
if (name) {
array[*idx] = cfstr2buf(name);
SAFE_CFRelease(name);
*idx += 1;
}
}
static void get_trait(CFDictionaryRef traits, CFStringRef attribute,
float *trait)
{
CFNumberRef cftrait = CFDictionaryGetValue(traits, attribute);
if (!CFNumberGetValue(cftrait, kCFNumberFloatType, trait))
*trait = 0.0;
}
static void get_font_traits(CTFontDescriptorRef fontd,
ASS_FontProviderMetaData *meta)
{
float weight, slant, width;
CFDictionaryRef traits =
CTFontDescriptorCopyAttribute(fontd, kCTFontTraitsAttribute);
get_trait(traits, kCTFontWeightTrait, &weight);
get_trait(traits, kCTFontSlantTrait, &slant);
get_trait(traits, kCTFontWidthTrait, &width);
SAFE_CFRelease(traits);
// Printed all of my system fonts (see if'deffed code below). Here is how
// CoreText 'normalized' weights maps to CSS/libass:
// opentype: 0 100 200 300 400 500 600 700 800 900
// css: LIGHT REG MED SBOLD BOLD BLACK EXTRABL
// libass: LIGHT MEDIUM BOLD
// coretext: -0.4 0.0 0.23 0.3 0.4 0.62
if (weight >= 0.62)
meta->weight = 800;
else if (weight >= 0.4)
meta->weight = 700;
else if (weight >= 0.3)
meta->weight = 600;
else if (weight >= 0.23)
meta->weight = 500;
else if (weight >= -0.4)
meta->weight = 400;
else
meta->weight = 200;
if (slant > 0.03)
meta->slant = FONT_SLANT_ITALIC;
else
meta->slant = FONT_SLANT_NONE;
if (width <= -0.2)
meta->width = FONT_WIDTH_CONDENSED;
else if (width >= 0.2)
meta->width = FONT_WIDTH_EXPANDED;
else
meta->width = FONT_WIDTH_NORMAL;
#if 0
char *name[1];
int idx = 0;
get_name(fontd, kCTFontDisplayNameAttribute, name, &idx);
char *file = get_font_file(fontd);
printf(
"Font traits for: %-40s [%-50s] "
"<slant: %f, %03d>, <weight: (%f, %03d)>, <width: %f, %03d>\n",
name[0], file,
slant, meta->slant, weight, meta->weight, width, meta->width);
free(name[0]);
free(file);
#endif
}
static void process_descriptors(ASS_FontProvider *provider, CFArrayRef fontsd)
{
ASS_FontProviderMetaData meta;
char *families[1];
char *identifiers[1];
char *fullnames[1];
if (!fontsd)
return;
for (int i = 0; i < CFArrayGetCount(fontsd); i++) {
CTFontDescriptorRef fontd = CFArrayGetValueAtIndex(fontsd, i);
int index = -1;
char *path = get_font_file(fontd);
if (strcmp("", path) == 0) {
// skip the font if the URL field in the font descriptor is empty
free(path);
continue;
}
memset(&meta, 0, sizeof(meta));
get_font_traits(fontd, &meta);
get_name(fontd, kCTFontFamilyNameAttribute, families, &meta.n_family);
meta.families = families;
get_name(fontd, kCTFontDisplayNameAttribute, fullnames, &meta.n_fullname);
meta.fullnames = fullnames;
int zero = 0;
get_name(fontd, kCTFontNameAttribute, identifiers, &zero);
meta.postscript_name = identifiers[0];
CFRetain(fontd);
ass_font_provider_add_font(provider, &meta, path, index, (void*)fontd);
for (int j = 0; j < meta.n_family; j++)
free(meta.families[j]);
for (int j = 0; j < meta.n_fullname; j++)
free(meta.fullnames[j]);
free(meta.postscript_name);
free(path);
}
}
static void match_fonts(ASS_Library *lib, ASS_FontProvider *provider,
char *name)
{
enum { attributes_n = 3 };
CTFontDescriptorRef ctdescrs[attributes_n];
CFMutableDictionaryRef cfattrs[attributes_n];
CFStringRef attributes[attributes_n] = {
kCTFontFamilyNameAttribute,
kCTFontDisplayNameAttribute,
kCTFontNameAttribute,
};
CFStringRef cfname =
CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
for (int i = 0; i < attributes_n; i++) {
cfattrs[i] = CFDictionaryCreateMutable(NULL, 0, 0, 0);
CFDictionaryAddValue(cfattrs[i], attributes[i], cfname);
ctdescrs[i] = CTFontDescriptorCreateWithAttributes(cfattrs[i]);
}
CFArrayRef descriptors =
CFArrayCreate(NULL, (const void **)&ctdescrs, attributes_n, NULL);
CTFontCollectionRef ctcoll =
CTFontCollectionCreateWithFontDescriptors(descriptors, 0);
CFArrayRef fontsd =
CTFontCollectionCreateMatchingFontDescriptors(ctcoll);
process_descriptors(provider, fontsd);
SAFE_CFRelease(fontsd);
SAFE_CFRelease(ctcoll);
for (int i = 0; i < attributes_n; i++) {
SAFE_CFRelease(cfattrs[i]);
SAFE_CFRelease(ctdescrs[i]);
}
SAFE_CFRelease(descriptors);
SAFE_CFRelease(cfname);
}
static char *get_fallback(void *priv, const char *family, uint32_t codepoint)
{
CFStringRef name = CFStringCreateWithBytes(
0, (UInt8 *)family, strlen(family), kCFStringEncodingUTF8, false);
CTFontRef font = CTFontCreateWithName(name, 0, NULL);
uint32_t codepointle = OSSwapHostToLittleInt32(codepoint);
CFStringRef r = CFStringCreateWithBytes(
0, (UInt8*)&codepointle, sizeof(codepointle),
kCFStringEncodingUTF32LE, false);
CTFontRef fb = CTFontCreateForString(font, r, CFRangeMake(0, 1));
CFStringRef cffamily = CTFontCopyFamilyName(fb);
char *res_family = cfstr2buf(cffamily);
SAFE_CFRelease(name);
SAFE_CFRelease(font);
SAFE_CFRelease(r);
SAFE_CFRelease(fb);
SAFE_CFRelease(cffamily);
return res_family;
}
static void get_substitutions(void *priv, const char *name,
ASS_FontProviderMetaData *meta)
{
const int n = sizeof(font_substitutions) / sizeof(font_substitutions[0]);
ass_map_font(font_substitutions, n, name, meta);
}
static ASS_FontProviderFuncs coretext_callbacks = {
.check_postscript = check_postscript,
.check_glyph = check_glyph,
.destroy_font = destroy_font,
.match_fonts = match_fonts,
.get_substitutions = get_substitutions,
.get_fallback = get_fallback,
};
ASS_FontProvider *
ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
const char *config)
{
return ass_font_provider_new(selector, &coretext_callbacks, NULL);
}

33
libass/ass_coretext.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "ass_types.h"
#include "ass_fontselect.h"
#ifndef ASS_CORETEXT_H
#define ASS_CORETEXT_H
#ifdef CONFIG_CORETEXT
ASS_FontProvider *
ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
const char *config);
#endif
#endif

800
libass/ass_directwrite.c Normal file
View File

@ -0,0 +1,800 @@
/*
* Copyright (C) 2015 Stephan Vedder <stephan.vedder@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define COBJMACROS
#include "config.h"
#include "ass_compat.h"
#include <initguid.h>
#include <ole2.h>
#include <shobjidl.h>
#include "dwrite_c.h"
#include "ass_directwrite.h"
#include "ass_utils.h"
#define NAME_MAX_LENGTH 256
#define FALLBACK_DEFAULT_FONT L"Arial"
static const ASS_FontMapping font_substitutions[] = {
{"sans-serif", "Arial"},
{"serif", "Times New Roman"},
{"monospace", "Courier New"}
};
/*
* The private data stored for every font, detected by this backend.
*/
typedef struct {
IDWriteFont *font;
IDWriteFontFace *face;
IDWriteFontFileStream *stream;
} FontPrivate;
typedef struct {
HMODULE directwrite_lib;
IDWriteFactory *factory;
} ProviderPrivate;
/**
* Custom text renderer class for logging the fonts used. It does not
* actually render anything or do anything apart from that.
*/
typedef struct FallbackLogTextRenderer {
IDWriteTextRenderer iface;
IDWriteTextRendererVtbl vtbl;
IDWriteFactory *dw_factory;
LONG ref_count;
} FallbackLogTextRenderer;
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_IsPixelSnappingDisabled(
IDWriteTextRenderer *This,
void* clientDrawingContext,
BOOL* isDisabled
)
{
*isDisabled = true;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_GetCurrentTransform(
IDWriteTextRenderer *This,
void* clientDrawingContext,
DWRITE_MATRIX* transform
)
{
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_GetPixelsPerDip(
IDWriteTextRenderer *This,
void* clientDrawingContext,
FLOAT* pixelsPerDip
)
{
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawGlyphRun(
IDWriteTextRenderer *This,
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
DWRITE_GLYPH_RUN const* glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
IUnknown* clientDrawingEffect
)
{
FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
HRESULT hr;
IDWriteFontCollection *font_coll = NULL;
IDWriteFont **font = (IDWriteFont **)clientDrawingContext;
hr = IDWriteFactory_GetSystemFontCollection(this->dw_factory, &font_coll, FALSE);
if (FAILED(hr))
return E_FAIL;
hr = IDWriteFontCollection_GetFontFromFontFace(font_coll, glyphRun->fontFace,
font);
if (FAILED(hr))
return E_FAIL;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawUnderline(
IDWriteTextRenderer *This,
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_UNDERLINE const* underline,
IUnknown* clientDrawingEffect
)
{
return S_OK;
}
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawStrikethrough(
IDWriteTextRenderer *This,
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_STRIKETHROUGH const* strikethrough,
IUnknown* clientDrawingEffect
)
{
return S_OK;
}
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawInlineObject(
IDWriteTextRenderer *This,
void *clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject *inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown *clientDrawingEffect
)
{
return S_OK;
}
// IUnknown methods
static ULONG STDMETHODCALLTYPE FallbackLogTextRenderer_AddRef(
IDWriteTextRenderer *This
)
{
FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
return InterlockedIncrement(&this->ref_count);
}
static ULONG STDMETHODCALLTYPE FallbackLogTextRenderer_Release(
IDWriteTextRenderer *This
)
{
FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
unsigned long new_count = InterlockedDecrement(&this->ref_count);
if (new_count == 0) {
free(this);
return 0;
}
return new_count;
}
static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_QueryInterface(
IDWriteTextRenderer *This,
REFIID riid,
void **ppvObject
)
{
if (IsEqualGUID(riid, &IID_IDWriteTextRenderer)
|| IsEqualGUID(riid, &IID_IDWritePixelSnapping)
|| IsEqualGUID(riid, &IID_IUnknown)) {
*ppvObject = This;
} else {
*ppvObject = NULL;
return E_FAIL;
}
This->lpVtbl->AddRef(This);
return S_OK;
}
static void init_FallbackLogTextRenderer(FallbackLogTextRenderer *r,
IDWriteFactory *factory)
{
*r = (FallbackLogTextRenderer){
.iface = {
.lpVtbl = &r->vtbl,
},
.vtbl = {
FallbackLogTextRenderer_QueryInterface,
FallbackLogTextRenderer_AddRef,
FallbackLogTextRenderer_Release,
FallbackLogTextRenderer_IsPixelSnappingDisabled,
FallbackLogTextRenderer_GetCurrentTransform,
FallbackLogTextRenderer_GetPixelsPerDip,
FallbackLogTextRenderer_DrawGlyphRun,
FallbackLogTextRenderer_DrawUnderline,
FallbackLogTextRenderer_DrawStrikethrough,
FallbackLogTextRenderer_DrawInlineObject,
},
.dw_factory = factory,
};
}
/*
* This function is called whenever a font is accessed for the
* first time. It will create a FontFace for metadata access and
* memory reading, which will be stored within the private data.
*/
static bool init_font_private_face(FontPrivate *priv)
{
HRESULT hr;
IDWriteFontFace *face;
if (priv->face != NULL)
return true;
hr = IDWriteFont_CreateFontFace(priv->font, &face);
if (FAILED(hr) || !face)
return false;
priv->face = face;
return true;
}
/*
* This function is called whenever a font is used for the first
* time. It will create a FontStream for memory reading, which
* will be stored within the private data.
*/
static bool init_font_private_stream(FontPrivate *priv)
{
HRESULT hr = S_OK;
IDWriteFontFile *file = NULL;
IDWriteFontFileStream *stream = NULL;
IDWriteFontFileLoader *loader = NULL;
UINT32 n_files = 1;
const void *refKey = NULL;
UINT32 keySize = 0;
if (priv->stream != NULL)
return true;
if (!init_font_private_face(priv))
return false;
/* DirectWrite only supports one file per face */
hr = IDWriteFontFace_GetFiles(priv->face, &n_files, &file);
if (FAILED(hr) || !file)
return false;
hr = IDWriteFontFile_GetReferenceKey(file, &refKey, &keySize);
if (FAILED(hr)) {
IDWriteFontFile_Release(file);
return false;
}
hr = IDWriteFontFile_GetLoader(file, &loader);
if (FAILED(hr) || !loader) {
IDWriteFontFile_Release(file);
return false;
}
hr = IDWriteFontFileLoader_CreateStreamFromKey(loader, refKey, keySize, &stream);
if (FAILED(hr) || !stream) {
IDWriteFontFile_Release(file);
return false;
}
priv->stream = stream;
IDWriteFontFile_Release(file);
return true;
}
/*
* Read a specified part of a fontfile into memory.
* If the font wasn't used before first creates a
* FontStream and save it into the private data for later usage.
* If the parameter "buf" is NULL libass wants to know the
* size of the Fontfile
*/
static size_t get_data(void *data, unsigned char *buf, size_t offset,
size_t length)
{
HRESULT hr = S_OK;
FontPrivate *priv = (FontPrivate *) data;
const void *fileBuf = NULL;
void *fragContext = NULL;
if (!init_font_private_stream(priv))
return 0;
if (buf == NULL) {
UINT64 fileSize;
hr = IDWriteFontFileStream_GetFileSize(priv->stream, &fileSize);
if (FAILED(hr))
return 0;
return fileSize;
}
hr = IDWriteFontFileStream_ReadFileFragment(priv->stream, &fileBuf, offset,
length, &fragContext);
if (FAILED(hr) || !fileBuf)
return 0;
memcpy(buf, fileBuf, length);
IDWriteFontFileStream_ReleaseFileFragment(priv->stream, fragContext);
return length;
}
/*
* Check whether the font contains PostScript outlines.
*/
static bool check_postscript(void *data)
{
FontPrivate *priv = (FontPrivate *) data;
if (!init_font_private_face(priv))
return false;
DWRITE_FONT_FACE_TYPE type = IDWriteFontFace_GetType(priv->face);
return type == DWRITE_FONT_FACE_TYPE_CFF ||
type == DWRITE_FONT_FACE_TYPE_RAW_CFF ||
type == DWRITE_FONT_FACE_TYPE_TYPE1;
}
/*
* Lazily return index of font. It requires the FontFace to be present, which is expensive to initialize.
*/
static unsigned get_font_index(void *data)
{
FontPrivate *priv = (FontPrivate *)data;
if (!init_font_private_face(priv))
return 0;
return IDWriteFontFace_GetIndex(priv->face);
}
/*
* Check if the passed font has a specific unicode character.
*/
static bool check_glyph(void *data, uint32_t code)
{
HRESULT hr = S_OK;
FontPrivate *priv = (FontPrivate *) data;
BOOL exists = FALSE;
if (code == 0)
return true;
hr = IDWriteFont_HasCharacter(priv->font, code, &exists);
if (FAILED(hr))
return false;
return exists;
}
/*
* This will release the directwrite backend
*/
static void destroy_provider(void *priv)
{
ProviderPrivate *provider_priv = (ProviderPrivate *)priv;
provider_priv->factory->lpVtbl->Release(provider_priv->factory);
FreeLibrary(provider_priv->directwrite_lib);
free(provider_priv);
}
/*
* This will destroy a specific font and it's
* Fontstream (in case it does exist)
*/
static void destroy_font(void *data)
{
FontPrivate *priv = (FontPrivate *) data;
IDWriteFont_Release(priv->font);
if (priv->face != NULL)
IDWriteFontFace_Release(priv->face);
if (priv->stream != NULL)
IDWriteFontFileStream_Release(priv->stream);
free(priv);
}
static int encode_utf16(wchar_t *chars, uint32_t codepoint)
{
if (codepoint < 0x10000) {
chars[0] = codepoint;
return 1;
} else {
chars[0] = (codepoint >> 10) + 0xD7C0;
chars[1] = (codepoint & 0x3FF) + 0xDC00;
return 2;
}
}
static char *get_fallback(void *priv, const char *base, uint32_t codepoint)
{
HRESULT hr;
ProviderPrivate *provider_priv = (ProviderPrivate *)priv;
IDWriteFactory *dw_factory = provider_priv->factory;
IDWriteTextFormat *text_format = NULL;
IDWriteTextLayout *text_layout = NULL;
FallbackLogTextRenderer renderer;
init_FallbackLogTextRenderer(&renderer, dw_factory);
hr = IDWriteFactory_CreateTextFormat(dw_factory, FALLBACK_DEFAULT_FONT, NULL,
DWRITE_FONT_WEIGHT_MEDIUM, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 1.0f, L"", &text_format);
if (FAILED(hr)) {
return NULL;
}
// Encode codepoint as UTF-16
wchar_t char_string[2];
int char_len = encode_utf16(char_string, codepoint);
// Create a text_layout, a high-level text rendering facility, using
// the given codepoint and dummy format.
hr = IDWriteFactory_CreateTextLayout(dw_factory, char_string, char_len, text_format,
0.0f, 0.0f, &text_layout);
if (FAILED(hr)) {
IDWriteTextFormat_Release(text_format);
return NULL;
}
// Draw the layout with a dummy renderer, which logs the
// font used and stores it.
IDWriteFont *font = NULL;
hr = IDWriteTextLayout_Draw(text_layout, &font, &renderer.iface, 0.0f, 0.0f);
if (FAILED(hr) || font == NULL) {
IDWriteTextLayout_Release(text_layout);
IDWriteTextFormat_Release(text_format);
return NULL;
}
// We're done with these now
IDWriteTextLayout_Release(text_layout);
IDWriteTextFormat_Release(text_format);
// Now, just extract the first family name
BOOL exists = FALSE;
IDWriteLocalizedStrings *familyNames = NULL;
hr = IDWriteFont_GetInformationalStrings(font,
DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES,
&familyNames, &exists);
if (FAILED(hr) || !exists) {
IDWriteFont_Release(font);
return NULL;
}
wchar_t temp_name[NAME_MAX_LENGTH];
hr = IDWriteLocalizedStrings_GetString(familyNames, 0, temp_name, NAME_MAX_LENGTH);
if (FAILED(hr)) {
IDWriteLocalizedStrings_Release(familyNames);
IDWriteFont_Release(font);
return NULL;
}
temp_name[NAME_MAX_LENGTH-1] = 0;
// DirectWrite may not have found a valid fallback, so check that
// the selected font actually has the requested glyph.
if (codepoint > 0) {
hr = IDWriteFont_HasCharacter(font, codepoint, &exists);
if (FAILED(hr) || !exists) {
IDWriteLocalizedStrings_Release(familyNames);
IDWriteFont_Release(font);
return NULL;
}
}
int size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0,NULL, NULL);
char *family = (char *) malloc(size_needed);
WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, family, size_needed, NULL, NULL);
IDWriteLocalizedStrings_Release(familyNames);
IDWriteFont_Release(font);
return family;
}
static int map_width(enum DWRITE_FONT_STRETCH stretch)
{
switch (stretch) {
case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: return 50;
case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: return 63;
case DWRITE_FONT_STRETCH_CONDENSED: return FONT_WIDTH_CONDENSED;
case DWRITE_FONT_STRETCH_SEMI_CONDENSED: return 88;
case DWRITE_FONT_STRETCH_MEDIUM: return FONT_WIDTH_NORMAL;
case DWRITE_FONT_STRETCH_SEMI_EXPANDED: return 113;
case DWRITE_FONT_STRETCH_EXPANDED: return FONT_WIDTH_EXPANDED;
case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: return 150;
case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: return 200;
default:
return FONT_WIDTH_NORMAL;
}
}
static void add_font(IDWriteFont *font, IDWriteFontFamily *fontFamily,
ASS_FontProvider *provider)
{
HRESULT hr;
BOOL exists;
wchar_t temp_name[NAME_MAX_LENGTH];
int size_needed;
ASS_FontProviderMetaData meta = {0};
meta.weight = IDWriteFont_GetWeight(font);
meta.width = map_width(IDWriteFont_GetStretch(font));
DWRITE_FONT_STYLE style = IDWriteFont_GetStyle(font);
meta.slant = (style == DWRITE_FONT_STYLE_NORMAL) ? FONT_SLANT_NONE :
(style == DWRITE_FONT_STYLE_OBLIQUE)? FONT_SLANT_OBLIQUE :
(style == DWRITE_FONT_STYLE_ITALIC) ? FONT_SLANT_ITALIC : FONT_SLANT_NONE;
IDWriteLocalizedStrings *psNames;
hr = IDWriteFont_GetInformationalStrings(font,
DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, &psNames, &exists);
if (FAILED(hr))
goto cleanup;
if (exists) {
hr = IDWriteLocalizedStrings_GetString(psNames, 0, temp_name, NAME_MAX_LENGTH);
if (FAILED(hr)) {
IDWriteLocalizedStrings_Release(psNames);
goto cleanup;
}
temp_name[NAME_MAX_LENGTH-1] = 0;
size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0, NULL, NULL);
char *mbName = (char *) malloc(size_needed);
if (!mbName) {
IDWriteLocalizedStrings_Release(psNames);
goto cleanup;
}
WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, mbName, size_needed, NULL, NULL);
meta.postscript_name = mbName;
IDWriteLocalizedStrings_Release(psNames);
}
IDWriteLocalizedStrings *fontNames;
hr = IDWriteFont_GetInformationalStrings(font,
DWRITE_INFORMATIONAL_STRING_FULL_NAME, &fontNames, &exists);
if (FAILED(hr))
goto cleanup;
if (exists) {
meta.n_fullname = IDWriteLocalizedStrings_GetCount(fontNames);
meta.fullnames = (char **) calloc(meta.n_fullname, sizeof(char *));
if (!meta.fullnames) {
IDWriteLocalizedStrings_Release(fontNames);
goto cleanup;
}
for (int k = 0; k < meta.n_fullname; k++) {
hr = IDWriteLocalizedStrings_GetString(fontNames, k,
temp_name,
NAME_MAX_LENGTH);
if (FAILED(hr)) {
IDWriteLocalizedStrings_Release(fontNames);
goto cleanup;
}
temp_name[NAME_MAX_LENGTH-1] = 0;
size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0, NULL, NULL);
char *mbName = (char *) malloc(size_needed);
if (!mbName) {
IDWriteLocalizedStrings_Release(fontNames);
goto cleanup;
}
WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, mbName, size_needed, NULL, NULL);
meta.fullnames[k] = mbName;
}
IDWriteLocalizedStrings_Release(fontNames);
}
IDWriteLocalizedStrings *familyNames;
hr = IDWriteFont_GetInformationalStrings(font,
DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &familyNames, &exists);
if (FAILED(hr) || !exists)
hr = IDWriteFontFamily_GetFamilyNames(fontFamily, &familyNames);
if (FAILED(hr))
goto cleanup;
meta.n_family = IDWriteLocalizedStrings_GetCount(familyNames);
meta.families = (char **) calloc(meta.n_family, sizeof(char *));
if (!meta.families) {
IDWriteLocalizedStrings_Release(familyNames);
goto cleanup;
}
for (int k = 0; k < meta.n_family; k++) {
hr = IDWriteLocalizedStrings_GetString(familyNames, k,
temp_name,
NAME_MAX_LENGTH);
if (FAILED(hr)) {
IDWriteLocalizedStrings_Release(familyNames);
goto cleanup;
}
temp_name[NAME_MAX_LENGTH-1] = 0;
size_needed = WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, NULL, 0, NULL, NULL);
char *mbName = (char *) malloc(size_needed);
if (!mbName) {
IDWriteLocalizedStrings_Release(familyNames);
goto cleanup;
}
WideCharToMultiByte(CP_UTF8, 0, temp_name, -1, mbName, size_needed, NULL, NULL);
meta.families[k] = mbName;
}
IDWriteLocalizedStrings_Release(familyNames);
FontPrivate *font_priv = (FontPrivate *) calloc(1, sizeof(*font_priv));
if (!font_priv)
goto cleanup;
font_priv->font = font;
font = NULL;
ass_font_provider_add_font(provider, &meta, NULL, 0, font_priv);
cleanup:
if (meta.families) {
for (int k = 0; k < meta.n_family; k++)
free(meta.families[k]);
free(meta.families);
}
if (meta.fullnames) {
for (int k = 0; k < meta.n_fullname; k++)
free(meta.fullnames[k]);
free(meta.fullnames);
}
free(meta.postscript_name);
if (font)
IDWriteFont_Release(font);
}
/*
* Scan every system font on the current machine and add it
* to the libass lookup. Stores the FontPrivate as private data
* for later memory reading
*/
static void scan_fonts(IDWriteFactory *factory,
ASS_FontProvider *provider)
{
HRESULT hr = S_OK;
IDWriteFontCollection *fontCollection = NULL;
IDWriteFont *font = NULL;
hr = IDWriteFactory_GetSystemFontCollection(factory, &fontCollection, FALSE);
if (FAILED(hr) || !fontCollection)
return;
UINT32 familyCount = IDWriteFontCollection_GetFontFamilyCount(fontCollection);
for (UINT32 i = 0; i < familyCount; ++i) {
IDWriteFontFamily *fontFamily = NULL;
hr = IDWriteFontCollection_GetFontFamily(fontCollection, i, &fontFamily);
if (FAILED(hr))
continue;
UINT32 fontCount = IDWriteFontFamily_GetFontCount(fontFamily);
for (UINT32 j = 0; j < fontCount; ++j) {
hr = IDWriteFontFamily_GetFont(fontFamily, j, &font);
if (FAILED(hr))
continue;
// Simulations for bold or oblique are sometimes synthesized by
// DirectWrite. We are only interested in physical fonts.
if (IDWriteFont_GetSimulations(font) != 0) {
IDWriteFont_Release(font);
continue;
}
add_font(font, fontFamily, provider);
}
IDWriteFontFamily_Release(fontFamily);
}
IDWriteFontCollection_Release(fontCollection);
}
static void get_substitutions(void *priv, const char *name,
ASS_FontProviderMetaData *meta)
{
const int n = sizeof(font_substitutions) / sizeof(font_substitutions[0]);
ass_map_font(font_substitutions, n, name, meta);
}
/*
* Called by libass when the provider should perform the
* specified task
*/
static ASS_FontProviderFuncs directwrite_callbacks = {
.get_data = get_data,
.check_postscript = check_postscript,
.check_glyph = check_glyph,
.destroy_font = destroy_font,
.destroy_provider = destroy_provider,
.get_substitutions = get_substitutions,
.get_fallback = get_fallback,
.get_font_index = get_font_index,
};
typedef HRESULT (WINAPI *DWriteCreateFactoryFn)(
DWRITE_FACTORY_TYPE factoryType,
REFIID iid,
IUnknown **factory
);
/*
* Register the directwrite provider. Upon registering
* scans all system fonts. The private data for this
* provider is IDWriteFactory
* On failure returns NULL
*/
ASS_FontProvider *ass_directwrite_add_provider(ASS_Library *lib,
ASS_FontSelector *selector,
const char *config)
{
HRESULT hr = S_OK;
IDWriteFactory *dwFactory = NULL;
ASS_FontProvider *provider = NULL;
DWriteCreateFactoryFn DWriteCreateFactoryPtr = NULL;
ProviderPrivate *priv = NULL;
HMODULE directwrite_lib = LoadLibraryW(L"Dwrite.dll");
if (!directwrite_lib)
goto cleanup;
DWriteCreateFactoryPtr = (DWriteCreateFactoryFn)GetProcAddress(directwrite_lib,
"DWriteCreateFactory");
if (!DWriteCreateFactoryPtr)
goto cleanup;
hr = DWriteCreateFactoryPtr(DWRITE_FACTORY_TYPE_SHARED,
&IID_IDWriteFactory,
(IUnknown **) (&dwFactory));
if (FAILED(hr) || !dwFactory) {
ass_msg(lib, MSGL_WARN, "Failed to initialize directwrite.");
dwFactory = NULL;
goto cleanup;
}
priv = (ProviderPrivate *)calloc(sizeof(*priv), 1);
if (!priv)
goto cleanup;
priv->directwrite_lib = directwrite_lib;
priv->factory = dwFactory;
provider = ass_font_provider_new(selector, &directwrite_callbacks, priv);
if (!provider)
goto cleanup;
scan_fonts(dwFactory, provider);
return provider;
cleanup:
free(priv);
if (dwFactory)
dwFactory->lpVtbl->Release(dwFactory);
if (directwrite_lib)
FreeLibrary(directwrite_lib);
return NULL;
}

29
libass/ass_directwrite.h Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2015 Stephan Vedder <stephan.vedder@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "ass_types.h"
#include "ass_fontselect.h"
#ifndef ASS_DIRECTWRITE_H
#define ASS_DIRECTWRITE_H
ASS_FontProvider *
ass_directwrite_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
const char *config);
#endif

365
libass/ass_drawing.c Normal file
View File

@ -0,0 +1,365 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <ft2build.h>
#include FT_OUTLINE_H
#include <math.h>
#include <stdbool.h>
#include <limits.h>
#include "ass_utils.h"
#include "ass_drawing.h"
#include "ass_font.h"
#define GLYPH_INITIAL_POINTS 100
#define GLYPH_INITIAL_SEGMENTS 100
/*
* \brief Prepare drawing for parsing. This just sets a few parameters.
*/
static void drawing_prepare(ASS_Drawing *drawing)
{
// Scaling parameters
drawing->point_scale_x = drawing->scale_x / (1 << (drawing->scale - 1));
drawing->point_scale_y = drawing->scale_y / (1 << (drawing->scale - 1));
}
/*
* \brief Finish a drawing. This only sets the horizontal advance according
* to the outline's bbox at the moment.
*/
static void drawing_finish(ASS_Drawing *drawing, bool raw_mode)
{
ASS_Rect bbox = drawing->cbox;
ASS_Outline *ol = &drawing->outline;
if (drawing->library)
ass_msg(drawing->library, MSGL_V,
"Parsed drawing with %d points and %d segments",
ol->n_points, ol->n_segments);
if (raw_mode)
return;
drawing->advance.x = bbox.x_max - bbox.x_min;
double pbo = drawing->pbo / (1 << (drawing->scale - 1));
drawing->desc = double_to_d6(pbo * drawing->scale_y);
drawing->asc = bbox.y_max - bbox.y_min - drawing->desc;
// Place it onto the baseline
for (size_t i = 0; i < ol->n_points; i++)
ol->points[i].y -= drawing->asc;
}
/*
* \brief Check whether a number of items on the list is available
*/
static int token_check_values(ASS_DrawingToken *token, int i, int type)
{
for (int j = 0; j < i; j++) {
if (!token || token->type != type) return 0;
token = token->next;
}
return 1;
}
/*
* \brief Tokenize a drawing string into a list of ASS_DrawingToken
* This also expands points for closing b-splines
*/
static ASS_DrawingToken *drawing_tokenize(char *str)
{
char *p = str;
int type = -1, is_set = 0;
double val;
ASS_Vector point = {0, 0};
ASS_DrawingToken *root = NULL, *tail = NULL, *spline_start = NULL;
while (p && *p) {
int got_coord = 0;
if (*p == 'c' && spline_start) {
// Close b-splines: add the first three points of the b-spline
// back to the end
if (token_check_values(spline_start->next, 2, TOKEN_B_SPLINE)) {
for (int i = 0; i < 3; i++) {
tail->next = calloc(1, sizeof(ASS_DrawingToken));
tail->next->prev = tail;
tail = tail->next;
tail->type = TOKEN_B_SPLINE;
tail->point = spline_start->point;
spline_start = spline_start->next;
}
spline_start = NULL;
}
} else if (!is_set && mystrtod(&p, &val)) {
point.x = double_to_d6(val);
is_set = 1;
got_coord = 1;
p--;
} else if (is_set == 1 && mystrtod(&p, &val)) {
point.y = double_to_d6(val);
is_set = 2;
got_coord = 1;
p--;
} else if (*p == 'm')
type = TOKEN_MOVE;
else if (*p == 'n')
type = TOKEN_MOVE_NC;
else if (*p == 'l')
type = TOKEN_LINE;
else if (*p == 'b')
type = TOKEN_CUBIC_BEZIER;
else if (*p == 'q')
type = TOKEN_CONIC_BEZIER;
else if (*p == 's')
type = TOKEN_B_SPLINE;
// We're simply ignoring TOKEN_EXTEND_B_SPLINE here.
// This is not harmful at all, since it can be ommitted with
// similar result (the spline is extended anyway).
// Ignore the odd extra value, it makes no sense.
if (!got_coord)
is_set = 0;
if (type != -1 && is_set == 2) {
if (root) {
tail->next = calloc(1, sizeof(ASS_DrawingToken));
tail->next->prev = tail;
tail = tail->next;
} else
root = tail = calloc(1, sizeof(ASS_DrawingToken));
tail->type = type;
tail->point = point;
is_set = 0;
if (type == TOKEN_B_SPLINE && !spline_start)
spline_start = tail->prev;
}
p++;
}
return root;
}
/*
* \brief Free a list of tokens
*/
static void drawing_free_tokens(ASS_DrawingToken *token)
{
while (token) {
ASS_DrawingToken *at = token;
token = token->next;
free(at);
}
}
/*
* \brief Translate and scale a point coordinate according to baseline
* offset and scale.
*/
static inline void translate_point(ASS_Drawing *drawing, ASS_Vector *point)
{
point->x = lrint(drawing->point_scale_x * point->x);
point->y = lrint(drawing->point_scale_y * point->y);
rectangle_update(&drawing->cbox, point->x, point->y, point->x, point->y);
}
/*
* \brief Add curve to drawing
*/
static bool drawing_add_curve(ASS_Drawing *drawing, ASS_DrawingToken *token,
bool spline, int started)
{
ASS_Vector p[4];
for (int i = 0; i < 4; ++i) {
p[i] = token->point;
translate_point(drawing, &p[i]);
token = token->next;
}
if (spline) {
int x01 = (p[1].x - p[0].x) / 3;
int y01 = (p[1].y - p[0].y) / 3;
int x12 = (p[2].x - p[1].x) / 3;
int y12 = (p[2].y - p[1].y) / 3;
int x23 = (p[3].x - p[2].x) / 3;
int y23 = (p[3].y - p[2].y) / 3;
p[0].x = p[1].x + ((x12 - x01) >> 1);
p[0].y = p[1].y + ((y12 - y01) >> 1);
p[3].x = p[2].x + ((x23 - x12) >> 1);
p[3].y = p[2].y + ((y23 - y12) >> 1);
p[1].x += x12;
p[1].y += y12;
p[2].x -= x12;
p[2].y -= y12;
}
return (started ||
outline_add_point(&drawing->outline, p[0], 0)) &&
outline_add_point(&drawing->outline, p[1], 0) &&
outline_add_point(&drawing->outline, p[2], 0) &&
outline_add_point(&drawing->outline, p[3], OUTLINE_CUBIC_SPLINE);
}
/*
* \brief Create and initialize a new drawing and return it
*/
ASS_Drawing *ass_drawing_new(ASS_Library *lib)
{
ASS_Drawing *drawing = calloc(1, sizeof(*drawing));
if (!drawing)
return NULL;
rectangle_reset(&drawing->cbox);
drawing->library = lib;
drawing->scale_x = 1.;
drawing->scale_y = 1.;
if (!outline_alloc(&drawing->outline, GLYPH_INITIAL_POINTS, GLYPH_INITIAL_SEGMENTS)) {
free(drawing);
return NULL;
}
return drawing;
}
/*
* \brief Free a drawing
*/
void ass_drawing_free(ASS_Drawing *drawing)
{
if (drawing) {
free(drawing->text);
outline_free(&drawing->outline);
}
free(drawing);
}
/*
* \brief Copy an ASCII string to the drawing text buffer
*/
void ass_drawing_set_text(ASS_Drawing *drawing, char *str, size_t len)
{
free(drawing->text);
drawing->text = strndup(str, len);
}
/*
* \brief Create a hashcode for the drawing
* XXX: To avoid collisions a better hash algorithm might be useful.
*/
void ass_drawing_hash(ASS_Drawing *drawing)
{
if (!drawing->text)
return;
drawing->hash = fnv_32a_str(drawing->text, FNV1_32A_INIT);
}
/*
* \brief Convert token list to outline. Calls the line and curve evaluators.
*/
ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, bool raw_mode)
{
bool started = false;
ASS_DrawingToken *token;
ASS_Vector pen = {0, 0};
drawing->tokens = drawing_tokenize(drawing->text);
drawing_prepare(drawing);
token = drawing->tokens;
while (token) {
// Draw something according to current command
switch (token->type) {
case TOKEN_MOVE_NC:
pen = token->point;
translate_point(drawing, &pen);
token = token->next;
break;
case TOKEN_MOVE:
pen = token->point;
translate_point(drawing, &pen);
if (started) {
if (!outline_add_segment(&drawing->outline, OUTLINE_LINE_SEGMENT))
goto error;
if (!outline_close_contour(&drawing->outline))
goto error;
started = false;
}
token = token->next;
break;
case TOKEN_LINE: {
ASS_Vector to = token->point;
translate_point(drawing, &to);
if (!started && !outline_add_point(&drawing->outline, pen, 0))
goto error;
if (!outline_add_point(&drawing->outline, to, OUTLINE_LINE_SEGMENT))
goto error;
started = true;
token = token->next;
break;
}
case TOKEN_CUBIC_BEZIER:
if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) &&
token->prev) {
if (!drawing_add_curve(drawing, token->prev, false, started))
goto error;
token = token->next;
token = token->next;
token = token->next;
started = true;
} else
token = token->next;
break;
case TOKEN_B_SPLINE:
if (token_check_values(token, 3, TOKEN_B_SPLINE) &&
token->prev) {
if (!drawing_add_curve(drawing, token->prev, true, started))
goto error;
token = token->next;
started = true;
} else
token = token->next;
break;
default:
token = token->next;
break;
}
}
// Close the last contour
if (started) {
if (!outline_add_segment(&drawing->outline, OUTLINE_LINE_SEGMENT))
goto error;
if (!outline_close_contour(&drawing->outline))
goto error;
}
drawing_finish(drawing, raw_mode);
drawing_free_tokens(drawing->tokens);
return &drawing->outline;
error:
drawing_free_tokens(drawing->tokens);
return NULL;
}

70
libass/ass_drawing.h Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_DRAWING_H
#define LIBASS_DRAWING_H
#include "ass.h"
#include "ass_outline.h"
#include "ass_bitmap.h"
typedef enum {
TOKEN_MOVE,
TOKEN_MOVE_NC,
TOKEN_LINE,
TOKEN_CUBIC_BEZIER,
TOKEN_CONIC_BEZIER,
TOKEN_B_SPLINE,
TOKEN_EXTEND_SPLINE,
TOKEN_CLOSE
} ASS_TokenType;
typedef struct ass_drawing_token {
ASS_TokenType type;
ASS_Vector point;
struct ass_drawing_token *next;
struct ass_drawing_token *prev;
} ASS_DrawingToken;
typedef struct {
char *text; // drawing string
int scale; // scale (1-64) for subpixel accuracy
double pbo; // drawing will be shifted in y direction by this amount
double scale_x; // FontScaleX
double scale_y; // FontScaleY
int asc; // ascender
int desc; // descender
ASS_Outline outline; // target outline
ASS_Vector advance; // advance (from cbox)
int hash; // hash value (for caching)
// private
ASS_Library *library;
ASS_DrawingToken *tokens; // tokenized drawing
double point_scale_x;
double point_scale_y;
ASS_Rect cbox; // bounding box, or let's say... VSFilter's idea of it
} ASS_Drawing;
ASS_Drawing *ass_drawing_new(ASS_Library *lib);
void ass_drawing_free(ASS_Drawing *drawing);
void ass_drawing_set_text(ASS_Drawing *drawing, char *str, size_t n);
void ass_drawing_hash(ASS_Drawing *drawing);
ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, bool raw_mode);
#endif /* LIBASS_DRAWING_H */

624
libass/ass_font.c Normal file
View File

@ -0,0 +1,624 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <inttypes.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SYNTHESIS_H
#include FT_GLYPH_H
#include FT_TRUETYPE_TABLES_H
#include FT_OUTLINE_H
#include <limits.h>
#include "ass.h"
#include "ass_library.h"
#include "ass_font.h"
#include "ass_fontselect.h"
#include "ass_utils.h"
#include "ass_shaper.h"
/**
* Select a good charmap, prefer Microsoft Unicode charmaps.
* Otherwise, let FreeType decide.
*/
void charmap_magic(ASS_Library *library, FT_Face face)
{
int i;
int ms_cmap = -1;
// Search for a Microsoft Unicode cmap
for (i = 0; i < face->num_charmaps; ++i) {
FT_CharMap cmap = face->charmaps[i];
unsigned pid = cmap->platform_id;
unsigned eid = cmap->encoding_id;
if (pid == 3 /*microsoft */
&& (eid == 1 /*unicode bmp */
|| eid == 10 /*full unicode */ )) {
FT_Set_Charmap(face, cmap);
return;
} else if (pid == 3 && ms_cmap < 0)
ms_cmap = i;
}
// Try the first Microsoft cmap if no Microsoft Unicode cmap was found
if (ms_cmap >= 0) {
FT_CharMap cmap = face->charmaps[ms_cmap];
FT_Set_Charmap(face, cmap);
return;
}
if (!face->charmap) {
if (face->num_charmaps == 0) {
ass_msg(library, MSGL_WARN, "Font face with no charmaps");
return;
}
ass_msg(library, MSGL_WARN,
"No charmap autodetected, trying the first one");
FT_Set_Charmap(face, face->charmaps[0]);
return;
}
}
/**
* Adjust char index if the charmap is weird
* (currently just MS Symbol)
*/
uint32_t ass_font_index_magic(FT_Face face, uint32_t symbol)
{
if (!face->charmap)
return symbol;
switch(face->charmap->encoding){
case FT_ENCODING_MS_SYMBOL:
return 0xF000 | symbol;
default:
return symbol;
}
}
static void buggy_font_workaround(FT_Face face)
{
// Some fonts have zero Ascender/Descender fields in 'hhea' table.
// In this case, get the information from 'os2' table or, as
// a last resort, from face.bbox.
if (face->ascender + face->descender == 0 || face->height == 0) {
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
if (os2) {
face->ascender = os2->sTypoAscender;
face->descender = os2->sTypoDescender;
face->height = face->ascender - face->descender;
} else {
face->ascender = face->bbox.yMax;
face->descender = face->bbox.yMin;
face->height = face->ascender - face->descender;
}
}
}
static unsigned long
read_stream_font(FT_Stream stream, unsigned long offset, unsigned char *buffer,
unsigned long count)
{
ASS_FontStream *font = (ASS_FontStream *)stream->descriptor.pointer;
font->func(font->priv, buffer, offset, count);
return count;
}
static void
close_stream_font(FT_Stream stream)
{
free(stream->descriptor.pointer);
free(stream);
}
/**
* \brief Select a face with the given charcode and add it to ASS_Font
* \return index of the new face in font->faces, -1 if failed
*/
static int add_face(ASS_FontSelector *fontsel, ASS_Font *font, uint32_t ch)
{
char *path;
char *postscript_name = NULL;
int i, index, uid, error;
ASS_FontStream stream = { NULL, NULL };
FT_Face face;
if (font->n_faces == ASS_FONT_MAX_FACES)
return -1;
path = ass_font_select(fontsel, font->library, font , &index,
&postscript_name, &uid, &stream, ch);
if (!path)
return -1;
for (i = 0; i < font->n_faces; i++) {
if (font->faces_uid[i] == uid) {
ass_msg(font->library, MSGL_INFO,
"Got a font face that already is available! Skipping.");
return i;
}
}
if (stream.func) {
FT_Open_Args args;
FT_Stream ftstream = calloc(1, sizeof(FT_StreamRec));
ASS_FontStream *fs = calloc(1, sizeof(ASS_FontStream));
*fs = stream;
ftstream->size = stream.func(stream.priv, NULL, 0, 0);
ftstream->read = read_stream_font;
ftstream->close = close_stream_font;
ftstream->descriptor.pointer = (void *)fs;
memset(&args, 0, sizeof(FT_Open_Args));
args.flags = FT_OPEN_STREAM;
args.stream = ftstream;
error = FT_Open_Face(font->ftlibrary, &args, index, &face);
if (error) {
ass_msg(font->library, MSGL_WARN,
"Error opening memory font: '%s'", path);
return -1;
}
} else {
error = FT_New_Face(font->ftlibrary, path, index, &face);
if (error) {
ass_msg(font->library, MSGL_WARN,
"Error opening font: '%s', %d", path, index);
return -1;
}
if (postscript_name && index < 0 && face->num_faces > 0) {
// The font provider gave us a post_script name and is not sure
// about the face index.. so use the postscript name to find the
// correct face_index in the collection!
for (int i = 0; i < face->num_faces; i++) {
FT_Done_Face(face);
error = FT_New_Face(font->ftlibrary, path, i, &face);
if (error) {
ass_msg(font->library, MSGL_WARN,
"Error opening font: '%s', %d", path, i);
return -1;
}
const char *face_psname = FT_Get_Postscript_Name(face);
if (face_psname != NULL &&
strcmp(face_psname, postscript_name) == 0)
break;
}
}
}
charmap_magic(font->library, face);
buggy_font_workaround(face);
font->faces[font->n_faces] = face;
font->faces_uid[font->n_faces++] = uid;
ass_face_set_size(face, font->size);
return font->n_faces - 1;
}
/**
* \brief Create a new ASS_Font according to "desc" argument
*/
ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
FT_Library ftlibrary, ASS_FontSelector *fontsel,
ASS_FontDesc *desc)
{
ASS_Font *font;
if (ass_cache_get(font_cache, desc, &font)) {
if (font->desc.family)
return font;
ass_cache_dec_ref(font);
return NULL;
}
if (!font)
return NULL;
font->library = library;
font->ftlibrary = ftlibrary;
font->shaper_priv = NULL;
font->n_faces = 0;
ASS_FontDesc *new_desc = ass_cache_key(font);
font->desc.family = new_desc->family;
font->desc.bold = desc->bold;
font->desc.italic = desc->italic;
font->desc.vertical = desc->vertical;
font->scale_x = font->scale_y = 1.;
font->v.x = font->v.y = 0;
font->size = 0.;
int error = add_face(fontsel, font, 0);
if (error == -1) {
font->desc.family = NULL;
ass_cache_commit(font, 1);
ass_cache_dec_ref(font);
return NULL;
}
ass_cache_commit(font, 1);
return font;
}
/**
* \brief Set font transformation matrix and shift vector
**/
void ass_font_set_transform(ASS_Font *font, double scale_x,
double scale_y, FT_Vector *v)
{
font->scale_x = scale_x;
font->scale_y = scale_y;
if (v) {
font->v.x = v->x;
font->v.y = v->y;
}
}
void ass_face_set_size(FT_Face face, double size)
{
TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
double mscale = 1.;
FT_Size_RequestRec rq;
FT_Size_Metrics *m = &face->size->metrics;
// VSFilter uses metrics from TrueType OS/2 table
// The idea was borrowed from asa (http://asa.diac24.net)
if (os2) {
int ft_height = 0;
if (hori)
ft_height = hori->Ascender - hori->Descender;
if (!ft_height)
ft_height = os2->sTypoAscender - os2->sTypoDescender;
/* sometimes used for signed values despite unsigned in spec */
int os2_height = (short)os2->usWinAscent + (short)os2->usWinDescent;
if (ft_height && os2_height)
mscale = (double) ft_height / os2_height;
}
memset(&rq, 0, sizeof(rq));
rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
rq.width = 0;
rq.height = double_to_d6(size * mscale);
rq.horiResolution = rq.vertResolution = 0;
FT_Request_Size(face, &rq);
m->ascender /= mscale;
m->descender /= mscale;
m->height /= mscale;
}
/**
* \brief Set font size
**/
void ass_font_set_size(ASS_Font *font, double size)
{
int i;
if (font->size != size) {
font->size = size;
for (i = 0; i < font->n_faces; ++i)
ass_face_set_size(font->faces[i], size);
}
}
/**
* \brief Get maximal font ascender and descender.
* \param ch character code
* The values are extracted from the font face that provides glyphs for the given character
**/
void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc,
int *desc)
{
int i;
for (i = 0; i < font->n_faces; ++i) {
FT_Face face = font->faces[i];
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
if (FT_Get_Char_Index(face, ass_font_index_magic(face, ch))) {
int y_scale = face->size->metrics.y_scale;
if (os2) {
*asc = FT_MulFix((short)os2->usWinAscent, y_scale);
*desc = FT_MulFix((short)os2->usWinDescent, y_scale);
} else {
*asc = FT_MulFix(face->ascender, y_scale);
*desc = FT_MulFix(-face->descender, y_scale);
}
return;
}
}
*asc = *desc = 0;
}
static void add_line(FT_Outline *ol, int bear, int advance, int dir, int pos, int size) {
FT_Vector points[4] = {
{.x = bear, .y = pos + size},
{.x = advance, .y = pos + size},
{.x = advance, .y = pos - size},
{.x = bear, .y = pos - size},
};
if (dir == FT_ORIENTATION_TRUETYPE) {
int i;
for (i = 0; i < 4; i++) {
ol->points[ol->n_points] = points[i];
ol->tags[ol->n_points++] = 1;
}
} else {
int i;
for (i = 3; i >= 0; i--) {
ol->points[ol->n_points] = points[i];
ol->tags[ol->n_points++] = 1;
}
}
ol->contours[ol->n_contours++] = ol->n_points - 1;
}
/*
* Strike a glyph with a horizontal line; it's possible to underline it
* and/or strike through it. For the line's position and size, truetype
* tables are consulted. Obviously this relies on the data in the tables
* being accurate.
*
*/
static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font,
FT_Glyph glyph, int under, int through)
{
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post);
FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline;
int advance, y_scale, i, dir;
if (!under && !through)
return 0;
// Grow outline
i = (under ? 4 : 0) + (through ? 4 : 0);
if (ol->n_points > SHRT_MAX - i)
return 0;
if (!ASS_REALLOC_ARRAY(ol->points, ol->n_points + i))
return 0;
if (!ASS_REALLOC_ARRAY(ol->tags, ol->n_points + i))
return 0;
i = !!under + !!through;
if (ol->n_contours > SHRT_MAX - i)
return 0;
if (!ASS_REALLOC_ARRAY(ol->contours, ol->n_contours + i))
return 0;
advance = d16_to_d6(glyph->advance.x);
y_scale = face->size->metrics.y_scale;
// Reverse drawing direction for non-truetype fonts
dir = FT_Outline_Get_Orientation(ol);
// Add points to the outline
if (under && ps) {
int pos = FT_MulFix(ps->underlinePosition, y_scale);
int size = FT_MulFix(ps->underlineThickness, y_scale / 2);
if (pos > 0 || size <= 0)
return 1;
add_line(ol, 0, advance, dir, pos, size);
}
if (through && os2) {
int pos = FT_MulFix(os2->yStrikeoutPosition, y_scale);
int size = FT_MulFix(os2->yStrikeoutSize, y_scale / 2);
if (pos < 0 || size <= 0)
return 1;
add_line(ol, 0, advance, dir, pos, size);
}
return 0;
}
/**
* Slightly embold a glyph without touching its metrics
*/
static void ass_glyph_embolden(FT_GlyphSlot slot)
{
int str;
if (slot->format != FT_GLYPH_FORMAT_OUTLINE)
return;
str = FT_MulFix(slot->face->units_per_EM,
slot->face->size->metrics.y_scale) / 64;
FT_Outline_Embolden(&slot->outline, str);
}
/**
* \brief Get glyph and face index
* Finds a face that has the requested codepoint and returns both face
* and glyph index.
*/
int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font,
uint32_t symbol, int *face_index, int *glyph_index)
{
int index = 0;
int i;
FT_Face face = 0;
*glyph_index = 0;
if (symbol < 0x20) {
*face_index = 0;
return 0;
}
// Handle NBSP like a regular space when rendering the glyph
if (symbol == 0xa0)
symbol = ' ';
if (font->n_faces == 0) {
*face_index = 0;
return 0;
}
// try with the requested face
if (*face_index < font->n_faces) {
face = font->faces[*face_index];
index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol));
}
// not found in requested face, try all others
for (i = 0; i < font->n_faces && index == 0; ++i) {
face = font->faces[i];
index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol));
if (index)
*face_index = i;
}
if (index == 0) {
int face_idx;
ass_msg(font->library, MSGL_INFO,
"Glyph 0x%X not found, selecting one more "
"font for (%s, %d, %d)", symbol, font->desc.family,
font->desc.bold, font->desc.italic);
face_idx = *face_index = add_face(fontsel, font, symbol);
if (face_idx >= 0) {
face = font->faces[face_idx];
index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol));
if (index == 0 && face->num_charmaps > 0) {
int i;
ass_msg(font->library, MSGL_WARN,
"Glyph 0x%X not found, broken font? Trying all charmaps", symbol);
for (i = 0; i < face->num_charmaps; i++) {
FT_Set_Charmap(face, face->charmaps[i]);
if ((index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol))) != 0) break;
}
}
if (index == 0) {
ass_msg(font->library, MSGL_ERR,
"Glyph 0x%X not found in font for (%s, %d, %d)",
symbol, font->desc.family, font->desc.bold,
font->desc.italic);
}
}
}
// FIXME: make sure we have a valid face_index. this is a HACK.
*face_index = FFMAX(*face_index, 0);
*glyph_index = index;
return 1;
}
/**
* \brief Get a glyph
* \param ch character code
**/
FT_Glyph ass_font_get_glyph(ASS_Font *font, uint32_t ch, int face_index,
int index, ASS_Hinting hinting, int deco)
{
int error;
FT_Glyph glyph;
FT_Face face = font->faces[face_index];
int flags = 0;
int vertical = font->desc.vertical;
flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
| FT_LOAD_IGNORE_TRANSFORM;
switch (hinting) {
case ASS_HINTING_NONE:
flags |= FT_LOAD_NO_HINTING;
break;
case ASS_HINTING_LIGHT:
flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
break;
case ASS_HINTING_NORMAL:
flags |= FT_LOAD_FORCE_AUTOHINT;
break;
case ASS_HINTING_NATIVE:
break;
}
error = FT_Load_Glyph(face, index, flags);
if (error) {
ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d",
index);
return 0;
}
if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) &&
(font->desc.italic > 55)) {
FT_GlyphSlot_Oblique(face->glyph);
}
if (!(face->style_flags & FT_STYLE_FLAG_BOLD) &&
(font->desc.bold > 400)) {
ass_glyph_embolden(face->glyph);
}
error = FT_Get_Glyph(face->glyph, &glyph);
if (error) {
ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d",
index);
return 0;
}
// Rotate glyph, if needed
if (vertical && ch >= VERTICAL_LOWER_BOUND) {
FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 };
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
int desc = 0;
if (os2)
desc = FT_MulFix(os2->sTypoDescender, face->size->metrics.y_scale);
FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 0, -desc);
FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m);
FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline,
face->glyph->metrics.vertAdvance, desc);
glyph->advance.x = face->glyph->linearVertAdvance;
}
ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
deco & DECO_STRIKETHROUGH);
// Apply scaling and shift
FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0,
double_to_d16(font->scale_y) };
FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline;
FT_Outline_Transform(outl, &scale);
FT_Outline_Translate(outl, font->v.x, font->v.y);
glyph->advance.x *= font->scale_x;
return glyph;
}
/**
* \brief Deallocate ASS_Font internals
**/
void ass_font_clear(ASS_Font *font)
{
int i;
if (font->shaper_priv)
ass_shaper_font_data_free(font->shaper_priv);
for (i = 0; i < font->n_faces; ++i) {
if (font->faces[i])
FT_Done_Face(font->faces[i]);
}
free(font->desc.family);
}

79
libass/ass_font.h Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_FONT_H
#define LIBASS_FONT_H
#include <stdint.h>
#include <ft2build.h>
#include FT_GLYPH_H
#include FT_OUTLINE_H
typedef struct ass_font ASS_Font;
typedef struct ass_font_desc ASS_FontDesc;
#include "ass.h"
#include "ass_types.h"
#include "ass_fontselect.h"
#include "ass_cache.h"
#define VERTICAL_LOWER_BOUND 0x02f1
#define ASS_FONT_MAX_FACES 10
#define DECO_UNDERLINE 1
#define DECO_STRIKETHROUGH 2
struct ass_font_desc {
char *family;
unsigned bold;
unsigned italic;
int vertical; // @font vertical layout
};
struct ass_font {
ASS_FontDesc desc;
ASS_Library *library;
FT_Library ftlibrary;
int faces_uid[ASS_FONT_MAX_FACES];
FT_Face faces[ASS_FONT_MAX_FACES];
ASS_ShaperFontData *shaper_priv;
int n_faces;
double scale_x, scale_y; // current transform
FT_Vector v; // current shift
double size;
};
void charmap_magic(ASS_Library *library, FT_Face face);
ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
FT_Library ftlibrary, ASS_FontSelector *fontsel,
ASS_FontDesc *desc);
void ass_font_set_transform(ASS_Font *font, double scale_x,
double scale_y, FT_Vector *v);
void ass_face_set_size(FT_Face face, double size);
void ass_font_set_size(ASS_Font *font, double size);
void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc,
int *desc);
int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font,
uint32_t symbol, int *face_index, int *glyph_index);
uint32_t ass_font_index_magic(FT_Face face, uint32_t symbol);
FT_Glyph ass_font_get_glyph(ASS_Font *font,
uint32_t ch, int face_index, int index,
ASS_Hinting hinting, int deco);
void ass_font_clear(ASS_Font *font);
#endif /* LIBASS_FONT_H */

322
libass/ass_fontconfig.c Normal file
View File

@ -0,0 +1,322 @@
/*
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fontconfig/fontconfig.h>
#include <fontconfig/fcfreetype.h>
#include "ass_fontconfig.h"
#include "ass_fontselect.h"
#include "ass_utils.h"
#define MAX_NAME 100
typedef struct fc_private {
FcConfig *config;
FcFontSet *fallbacks;
FcCharSet *fallback_chars;
} ProviderPrivate;
static bool check_postscript(void *priv)
{
FcPattern *pat = (FcPattern *)priv;
char *format;
FcResult result =
FcPatternGetString(pat, FC_FONTFORMAT, 0, (FcChar8 **)&format);
if (result != FcResultMatch)
return false;
return !strcmp(format, "Type 1") || !strcmp(format, "Type 42") ||
!strcmp(format, "CID Type 1") || !strcmp(format, "CFF");
}
static bool check_glyph(void *priv, uint32_t code)
{
FcPattern *pat = (FcPattern *)priv;
FcCharSet *charset;
if (!pat)
return true;
if (code == 0)
return true;
FcResult result = FcPatternGetCharSet(pat, FC_CHARSET, 0, &charset);
if (result != FcResultMatch)
return false;
if (FcCharSetHasChar(charset, code) == FcTrue)
return true;
return false;
}
static void destroy(void *priv)
{
ProviderPrivate *fc = (ProviderPrivate *)priv;
if (fc->fallback_chars)
FcCharSetDestroy(fc->fallback_chars);
if (fc->fallbacks)
FcFontSetDestroy(fc->fallbacks);
FcConfigDestroy(fc->config);
free(fc);
}
static void scan_fonts(FcConfig *config, ASS_FontProvider *provider)
{
int i;
FcFontSet *fonts;
ASS_FontProviderMetaData meta;
// get list of fonts
fonts = FcConfigGetFonts(config, FcSetSystem);
// fill font_info list
for (i = 0; i < fonts->nfont; i++) {
FcPattern *pat = fonts->fonts[i];
FcBool outline;
int index, weight;
char *path;
char *fullnames[MAX_NAME];
char *families[MAX_NAME];
// skip non-outline fonts
FcResult result = FcPatternGetBool(pat, FC_OUTLINE, 0, &outline);
if (result != FcResultMatch || outline != FcTrue)
continue;
// simple types
result = FcPatternGetInteger(pat, FC_SLANT, 0, &meta.slant);
result |= FcPatternGetInteger(pat, FC_WIDTH, 0, &meta.width);
result |= FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight);
result |= FcPatternGetInteger(pat, FC_INDEX, 0, &index);
if (result != FcResultMatch)
continue;
// fontconfig uses its own weight scale, apparently derived
// from typographical weight. we're using truetype weights, so
// convert appropriately
if (weight <= FC_WEIGHT_LIGHT)
meta.weight = FONT_WEIGHT_LIGHT;
else if (weight <= FC_WEIGHT_MEDIUM)
meta.weight = FONT_WEIGHT_MEDIUM;
else
meta.weight = FONT_WEIGHT_BOLD;
// path
result = FcPatternGetString(pat, FC_FILE, 0, (FcChar8 **)&path);
if (result != FcResultMatch)
continue;
// read family names
meta.n_family = 0;
while (FcPatternGetString(pat, FC_FAMILY, meta.n_family,
(FcChar8 **)&families[meta.n_family]) == FcResultMatch
&& meta.n_family < MAX_NAME)
meta.n_family++;
meta.families = families;
// read fullnames
meta.n_fullname = 0;
while (FcPatternGetString(pat, FC_FULLNAME, meta.n_fullname,
(FcChar8 **)&fullnames[meta.n_fullname]) == FcResultMatch
&& meta.n_fullname < MAX_NAME)
meta.n_fullname++;
meta.fullnames = fullnames;
// read PostScript name
result = FcPatternGetString(pat, FC_POSTSCRIPT_NAME, 0,
(FcChar8 **)&meta.postscript_name);
if (result != FcResultMatch)
meta.postscript_name = NULL;
ass_font_provider_add_font(provider, &meta, path, index, (void *)pat);
}
}
static void cache_fallbacks(ProviderPrivate *fc)
{
FcResult result;
if (fc->fallbacks)
return;
// Create a suitable pattern
FcPattern *pat = FcPatternCreate();
FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)"sans-serif");
FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
FcConfigSubstitute (fc->config, pat, FcMatchPattern);
FcDefaultSubstitute (pat);
// FC_LANG is automatically set according to locale, but this results
// in strange sorting sometimes, so remove the attribute completely.
FcPatternDel(pat, FC_LANG);
// Sort installed fonts and eliminate duplicates; this can be very
// expensive.
fc->fallbacks = FcFontSort(fc->config, pat, FcTrue, &fc->fallback_chars,
&result);
// If this fails, just add an empty set
if (result != FcResultMatch)
fc->fallbacks = FcFontSetCreate();
FcPatternDestroy(pat);
}
static char *get_fallback(void *priv, const char *family, uint32_t codepoint)
{
ProviderPrivate *fc = (ProviderPrivate *)priv;
FcResult result;
cache_fallbacks(fc);
if (!fc->fallbacks || fc->fallbacks->nfont == 0)
return NULL;
if (codepoint == 0) {
char *family = NULL;
result = FcPatternGetString(fc->fallbacks->fonts[0], FC_FAMILY, 0,
(FcChar8 **)&family);
if (result == FcResultMatch) {
return strdup(family);
} else {
return NULL;
}
}
// fallback_chars is the union of all available charsets, so
// if we can't find the glyph in there, the system does not
// have any font to render this glyph.
if (FcCharSetHasChar(fc->fallback_chars, codepoint) == FcFalse)
return NULL;
for (int j = 0; j < fc->fallbacks->nfont; j++) {
FcPattern *pattern = fc->fallbacks->fonts[j];
FcCharSet *charset;
result = FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charset);
if (result == FcResultMatch && FcCharSetHasChar(charset,
codepoint)) {
char *family = NULL;
result = FcPatternGetString(pattern, FC_FAMILY, 0,
(FcChar8 **)&family);
if (result == FcResultMatch) {
return strdup(family);
} else {
return NULL;
}
}
}
// we shouldn't get here
return NULL;
}
static void get_substitutions(void *priv, const char *name,
ASS_FontProviderMetaData *meta)
{
ProviderPrivate *fc = (ProviderPrivate *)priv;
FcPattern *pat = FcPatternCreate();
if (!pat)
return;
FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)name);
FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)"__libass_delimiter");
FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
if (!FcConfigSubstitute(fc->config, pat, FcMatchPattern))
goto cleanup;
// read and strdup fullnames
meta->n_fullname = 0;
meta->fullnames = calloc(MAX_NAME, sizeof(char *));
if (!meta->fullnames)
goto cleanup;
char *alias = NULL;
while (FcPatternGetString(pat, FC_FAMILY, meta->n_fullname,
(FcChar8 **)&alias) == FcResultMatch
&& meta->n_fullname < MAX_NAME
&& strcmp(alias, "__libass_delimiter") != 0) {
alias = strdup(alias);
if (!alias)
goto cleanup;
meta->fullnames[meta->n_fullname] = alias;
meta->n_fullname++;
}
cleanup:
FcPatternDestroy(pat);
}
static ASS_FontProviderFuncs fontconfig_callbacks = {
.check_postscript = check_postscript,
.check_glyph = check_glyph,
.destroy_provider = destroy,
.get_substitutions = get_substitutions,
.get_fallback = get_fallback,
};
ASS_FontProvider *
ass_fontconfig_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
const char *config)
{
int rc;
ProviderPrivate *fc = NULL;
ASS_FontProvider *provider = NULL;
fc = calloc(1, sizeof(ProviderPrivate));
if (fc == NULL)
return NULL;
// build and load fontconfig configuration
fc->config = FcConfigCreate();
rc = FcConfigParseAndLoad(fc->config, (unsigned char *) config, FcTrue);
if (!rc) {
ass_msg(lib, MSGL_WARN, "No usable fontconfig configuration "
"file found, using fallback.");
FcConfigDestroy(fc->config);
fc->config = FcInitLoadConfig();
}
if (fc->config)
rc = FcConfigBuildFonts(fc->config);
if (!rc || !fc->config) {
ass_msg(lib, MSGL_FATAL,
"No valid fontconfig configuration found!");
FcConfigDestroy(fc->config);
free(fc);
return NULL;
}
// create font provider
provider = ass_font_provider_new(selector, &fontconfig_callbacks, fc);
// build database from system fonts
scan_fonts(fc->config, provider);
return provider;
}

33
libass/ass_fontconfig.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "ass_types.h"
#include "ass_fontselect.h"
#ifndef ASS_FONTCONFIG_H
#define ASS_FONTCONFIG_H
#ifdef CONFIG_FONTCONFIG
ASS_FontProvider *
ass_fontconfig_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
const char *config);
#endif
#endif

1089
libass/ass_fontselect.c Normal file

File diff suppressed because it is too large Load Diff

276
libass/ass_fontselect.h Normal file
View File

@ -0,0 +1,276 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_FONTSELECT_H
#define LIBASS_FONTSELECT_H
#include <stdbool.h>
#include <stdint.h>
#include <ft2build.h>
#include FT_FREETYPE_H
typedef struct ass_shaper_font_data ASS_ShaperFontData;
typedef struct font_selector ASS_FontSelector;
typedef struct font_info ASS_FontInfo;
#include "ass_types.h"
#include "ass.h"
#include "ass_font.h"
typedef struct font_provider ASS_FontProvider;
/* Font Provider */
typedef struct ass_font_provider_meta_data ASS_FontProviderMetaData;
/**
* Get font data. This is a stream interface which can be used as an
* alternative to providing a font path (which may not be available).
*
* This is called by fontselect if a given font was added without a
* font path (i.e. the path was set to NULL).
*
* \param font_priv font private data
* \param output buffer; set to NULL to query stream size
* \param offset stream offset
* \param len bytes to read into output buffer from stream
* \return actual number of bytes read, or stream size if data == NULL
*/
typedef size_t (*GetDataFunc)(void *font_priv, unsigned char *data,
size_t offset, size_t len);
/**
* Check whether the font contains PostScript outlines.
*
* \param font_priv font private data
* \return true if the font contains PostScript outlines
*/
typedef bool (*CheckPostscriptFunc)(void *font_priv);
/**
* Check if a glyph is supported by a font.
*
* \param font_priv font private data
* \param codepont Unicode codepoint (UTF-32)
* \return true if codepoint is supported by the font
*/
typedef bool (*CheckGlyphFunc)(void *font_priv, uint32_t codepoint);
/**
* Get index of a font in context of a font collection.
* This function is optional and may be needed to initialize the font index
* lazily.
*
* \param font_priv font private data
* \return font index inside the collection, or 0 in case of a single font
*/
typedef unsigned (*GetFontIndex)(void *font_priv);
/**
* Destroy a font's private data.
*
* \param font_priv font private data
*/
typedef void (*DestroyFontFunc)(void *font_priv);
/**
* Destroy a font provider's private data.
*
* \param priv font provider private data
*/
typedef void (*DestroyProviderFunc)(void *priv);
/**
* Add fonts for a given font name; this should add all fonts matching the
* given name to the fontselect database.
*
* This is called by fontselect whenever a new logical font is created. The
* font provider set as default is used.
*
* \param lib ASS_Library instance
* \param provider font provider instance
* \param name font name (as specified in script)
*/
typedef void (*MatchFontsFunc)(ASS_Library *lib,
ASS_FontProvider *provider,
char *name);
/**
* Substitute font name by another. This implements generic font family
* substitutions (e.g. sans-serif, serif, monospace) as well as font aliases.
*
* The generic families should map to sensible platform-specific font families.
* Aliases are sometimes used to map from common fonts that don't exist on
* a particular platform to similar alternatives. For example, a Linux
* system with fontconfig may map "Arial" to "Liberation Sans" and Windows
* maps "Helvetica" to "Arial".
*
* This is called by fontselect when a new logical font is created. The font
* provider set as default is used.
*
* \param priv font provider private data
* \param name input string for substitution, as specified in the script
* \param meta metadata (fullnames and n_fullname) to be filled in
*/
typedef void (*SubstituteFontFunc)(void *priv, const char *name,
ASS_FontProviderMetaData *meta);
/**
* Get an appropriate fallback font for a given codepoint.
*
* This is called by fontselect whenever a glyph is not found in the
* physical font list of a logical font. fontselect will try to add the
* font family with match_fonts if it does not exist in the font list
* add match_fonts is not NULL. Note that the returned font family should
* contain the requested codepoint.
*
* Note that fontselect uses the font provider set as default to determine
* fallbacks.
*
* \param priv font provider private data
* \param family original font family name (try matching a similar font) (never NULL)
* \param codepoint Unicode codepoint (UTF-32)
* \return output font family, allocated with malloc(), must be freed
* by caller.
*/
typedef char *(*GetFallbackFunc)(void *priv,
const char *family,
uint32_t codepoint);
typedef struct font_provider_funcs {
GetDataFunc get_data; /* optional/mandatory */
CheckPostscriptFunc check_postscript; /* mandatory */
CheckGlyphFunc check_glyph; /* mandatory */
DestroyFontFunc destroy_font; /* optional */
DestroyProviderFunc destroy_provider; /* optional */
MatchFontsFunc match_fonts; /* optional */
SubstituteFontFunc get_substitutions; /* optional */
GetFallbackFunc get_fallback; /* optional */
GetFontIndex get_font_index; /* optional */
} ASS_FontProviderFuncs;
/*
* Basic font metadata. All strings must be encoded with UTF-8.
* At minimum one family is required.
*/
struct ass_font_provider_meta_data {
/**
* List of localized font family names, e.g. "Arial".
*/
char **families;
/**
* List of localized full names, e.g. "Arial Bold".
* The English name should be listed first to speed up typical matching.
*/
char **fullnames;
/**
* The PostScript name, e.g. "Arial-BoldMT".
*/
char *postscript_name;
int n_family; // Number of localized family names
int n_fullname; // Number of localized full names
int slant; // Font slant value from FONT_SLANT_*
int weight; // Font weight in TrueType scale, 100-900
// See FONT_WEIGHT_*
int width; // Font weight in percent, normally 100
// See FONT_WIDTH_*
};
typedef struct ass_font_stream ASS_FontStream;
struct ass_font_stream {
// GetDataFunc
size_t (*func)(void *font_priv, unsigned char *data,
size_t offset, size_t len);
void *priv;
};
typedef struct ass_font_mapping ASS_FontMapping;
struct ass_font_mapping {
const char *from;
const char *to;
};
/**
* Simple font substitution helper. This can be used to implement basic
* mappings from one name to another. This is useful for supporting
* generic font families in font providers.
*
* \param map list of mappings
* \param len length of list of mappings
* \param name font name to map from
* \param meta metadata struct, mapped fonts will be stored into this
*/
void ass_map_font(const ASS_FontMapping *map, int len, const char *name,
ASS_FontProviderMetaData *meta);
ASS_FontSelector *
ass_fontselect_init(ASS_Library *library,
FT_Library ftlibrary, const char *family,
const char *path, const char *config,
ASS_DefaultFontProvider dfp);
char *ass_font_select(ASS_FontSelector *priv, ASS_Library *library,
ASS_Font *font, int *index, char **postscript_name,
int *uid, ASS_FontStream *data, uint32_t code);
void ass_fontselect_free(ASS_FontSelector *priv);
// Font provider functions
ASS_FontProvider *ass_font_provider_new(ASS_FontSelector *selector,
ASS_FontProviderFuncs *funcs, void *data);
/**
* \brief Create an empty font provider. A font provider can be used to
* provide additional fonts to libass.
* \param priv parent renderer
* \param funcs callback functions
* \param private data for provider callbacks
*
*/
ASS_FontProvider *
ass_create_font_provider(ASS_Renderer *priv, ASS_FontProviderFuncs *funcs,
void *data);
/**
* \brief Add a font to a font provider.
* \param provider the font provider
* \param meta font metadata. See struct definition for more information.
* \param path absolute path to font, or NULL for memory-based fonts
* \param index index inside a font collection file
* (-1 to look up by PostScript name)
* \param data private data for font callbacks
* \return success
*
*/
bool
ass_font_provider_add_font(ASS_FontProvider *provider,
ASS_FontProviderMetaData *meta, const char *path,
int index, void *data);
/**
* \brief Free font provider and associated fonts.
* \param provider the font provider
*
*/
void ass_font_provider_free(ASS_FontProvider *provider);
#endif /* LIBASS_FONTSELECT_H */

132
libass/ass_func_template.h Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2015 Vabishchevich Nikolay <vabnick@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
void DECORATE(fill_solid_tile16)(uint8_t *buf, ptrdiff_t stride, int set);
void DECORATE(fill_solid_tile32)(uint8_t *buf, ptrdiff_t stride, int set);
void DECORATE(fill_halfplane_tile16)(uint8_t *buf, ptrdiff_t stride,
int32_t a, int32_t b, int64_t c, int32_t scale);
void DECORATE(fill_halfplane_tile32)(uint8_t *buf, ptrdiff_t stride,
int32_t a, int32_t b, int64_t c, int32_t scale);
void DECORATE(fill_generic_tile16)(uint8_t *buf, ptrdiff_t stride,
const struct segment *line, size_t n_lines,
int winding);
void DECORATE(fill_generic_tile32)(uint8_t *buf, ptrdiff_t stride,
const struct segment *line, size_t n_lines,
int winding);
void DECORATE(add_bitmaps)(uint8_t *dst, intptr_t dst_stride,
uint8_t *src, intptr_t src_stride,
intptr_t height, intptr_t width);
void DECORATE(sub_bitmaps)(uint8_t *dst, intptr_t dst_stride,
uint8_t *src, intptr_t src_stride,
intptr_t height, intptr_t width);
void DECORATE(mul_bitmaps)(uint8_t *dst, intptr_t dst_stride,
uint8_t *src1, intptr_t src1_stride,
uint8_t *src2, intptr_t src2_stride,
intptr_t width, intptr_t height);
void DECORATE(be_blur)(uint8_t *buf, intptr_t w, intptr_t h,
intptr_t stride, uint16_t *tmp);
void DECORATE(stripe_unpack)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride,
uintptr_t width, uintptr_t height);
void DECORATE(stripe_pack)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src,
uintptr_t width, uintptr_t height);
void DECORATE(shrink_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(shrink_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(expand_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(expand_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(pre_blur1_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(pre_blur1_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(pre_blur2_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(pre_blur2_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(pre_blur3_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(pre_blur3_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height);
void DECORATE(blur1234_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param);
void DECORATE(blur1234_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param);
void DECORATE(blur1235_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param);
void DECORATE(blur1235_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param);
void DECORATE(blur1246_horz)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param);
void DECORATE(blur1246_vert)(int16_t *dst, const int16_t *src,
uintptr_t src_width, uintptr_t src_height,
const int16_t *param);
const BitmapEngine DECORATE(bitmap_engine) = {
.align_order = ALIGN,
#if CONFIG_LARGE_TILES
.tile_order = 5,
.fill_solid = DECORATE(fill_solid_tile32),
.fill_halfplane = DECORATE(fill_halfplane_tile32),
.fill_generic = DECORATE(fill_generic_tile32),
#else
.tile_order = 4,
.fill_solid = DECORATE(fill_solid_tile16),
.fill_halfplane = DECORATE(fill_halfplane_tile16),
.fill_generic = DECORATE(fill_generic_tile16),
#endif
.add_bitmaps = DECORATE(add_bitmaps),
#ifdef __x86_64__
.sub_bitmaps = DECORATE(sub_bitmaps),
.mul_bitmaps = DECORATE(mul_bitmaps),
#else
.sub_bitmaps = ass_sub_bitmaps_c,
.mul_bitmaps = ass_mul_bitmaps_c,
#endif
#ifdef __x86_64__
.be_blur = DECORATE(be_blur),
#else
.be_blur = ass_be_blur_c,
#endif
.stripe_unpack = DECORATE(stripe_unpack),
.stripe_pack = DECORATE(stripe_pack),
.shrink_horz = DECORATE(shrink_horz),
.shrink_vert = DECORATE(shrink_vert),
.expand_horz = DECORATE(expand_horz),
.expand_vert = DECORATE(expand_vert),
.pre_blur_horz = { DECORATE(pre_blur1_horz), DECORATE(pre_blur2_horz), DECORATE(pre_blur3_horz) },
.pre_blur_vert = { DECORATE(pre_blur1_vert), DECORATE(pre_blur2_vert), DECORATE(pre_blur3_vert) },
.main_blur_horz = { DECORATE(blur1234_horz), DECORATE(blur1235_horz), DECORATE(blur1246_horz) },
.main_blur_vert = { DECORATE(blur1234_vert), DECORATE(blur1235_vert), DECORATE(blur1246_vert) },
};

164
libass/ass_library.c Normal file
View File

@ -0,0 +1,164 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "ass.h"
#include "ass_library.h"
#include "ass_utils.h"
#include "ass_string.h"
static void ass_msg_handler(int level, const char *fmt, va_list va, void *data)
{
if (level > MSGL_INFO)
return;
fprintf(stderr, "[ass] ");
vfprintf(stderr, fmt, va);
fprintf(stderr, "\n");
}
ASS_Library *ass_library_init(void)
{
ASS_Library* lib = calloc(1, sizeof(*lib));
if (lib)
lib->msg_callback = ass_msg_handler;
return lib;
}
void ass_library_done(ASS_Library *priv)
{
if (priv) {
ass_set_fonts_dir(priv, NULL);
ass_set_style_overrides(priv, NULL);
ass_clear_fonts(priv);
free(priv);
}
}
void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir)
{
free(priv->fonts_dir);
priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0;
}
void ass_set_extract_fonts(ASS_Library *priv, int extract)
{
priv->extract_fonts = !!extract;
}
void ass_set_style_overrides(ASS_Library *priv, char **list)
{
char **p;
char **q;
int cnt;
if (priv->style_overrides) {
for (p = priv->style_overrides; *p; ++p)
free(*p);
}
free(priv->style_overrides);
priv->style_overrides = NULL;
if (!list)
return;
for (p = list, cnt = 0; *p; ++p, ++cnt) {
}
priv->style_overrides = calloc(cnt + 1, sizeof(char *));
if (!priv->style_overrides)
return;
for (p = list, q = priv->style_overrides; *p; ++p, ++q)
*q = strdup(*p);
}
static int grow_array(void **array, int nelem, size_t elsize)
{
if (!(nelem & 31)) {
void *ptr = realloc(*array, (nelem + 32) * elsize);
if (!ptr)
return 0;
*array = ptr;
}
return 1;
}
void ass_add_font(ASS_Library *priv, char *name, char *data, int size)
{
int idx = priv->num_fontdata;
if (!name || !data || !size)
return;
if (!grow_array((void **) &priv->fontdata, priv->num_fontdata,
sizeof(*priv->fontdata)))
return;
priv->fontdata[idx].name = strdup(name);
priv->fontdata[idx].data = malloc(size);
if (!priv->fontdata[idx].name || !priv->fontdata[idx].data)
goto error;
memcpy(priv->fontdata[idx].data, data, size);
priv->fontdata[idx].size = size;
priv->num_fontdata++;
return;
error:
free(priv->fontdata[idx].name);
free(priv->fontdata[idx].data);
}
void ass_clear_fonts(ASS_Library *priv)
{
int i;
for (i = 0; i < priv->num_fontdata; ++i) {
free(priv->fontdata[i].name);
free(priv->fontdata[i].data);
}
free(priv->fontdata);
priv->fontdata = NULL;
priv->num_fontdata = 0;
}
/*
* Register a message callback function with libass. Without setting one,
* a default handler is used which prints everything with MSGL_INFO or
* higher to the standard output.
*
* \param msg_cb the callback function
* \param data additional data that will be passed to the callback
*/
void ass_set_message_cb(ASS_Library *priv,
void (*msg_cb)(int, const char *, va_list, void *),
void *data)
{
if (msg_cb) {
priv->msg_callback = msg_cb;
priv->msg_callback_data = data;
}
}

43
libass/ass_library.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_LIBRARY_H
#define LIBASS_LIBRARY_H
#include <stdarg.h>
typedef struct {
char *name;
char *data;
int size;
} ASS_Fontdata;
struct ass_library {
char *fonts_dir;
int extract_fonts;
char **style_overrides;
ASS_Fontdata *fontdata;
int num_fontdata;
void (*msg_callback)(int, const char *, va_list, void *);
void *msg_callback_data;
};
char *read_file(struct ass_library *library, char *fname, size_t *bufsize);
#endif /* LIBASS_LIBRARY_H */

1382
libass/ass_outline.c Normal file

File diff suppressed because it is too large Load Diff

106
libass/ass_outline.h Normal file
View File

@ -0,0 +1,106 @@
/*
* Copyright (C) 2016 Vabishchevich Nikolay <vabnick@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_OUTLINE_H
#define LIBASS_OUTLINE_H
#include <ft2build.h>
#include FT_OUTLINE_H
#include <stdbool.h>
#include <stdint.h>
#include "ass_utils.h"
typedef struct {
int32_t x, y;
} ASS_Vector;
typedef struct {
double x, y;
} ASS_DVector;
typedef struct {
int32_t x_min, y_min, x_max, y_max;
} ASS_Rect;
typedef struct {
double x_min, y_min, x_max, y_max;
} ASS_DRect;
static inline void rectangle_reset(ASS_Rect *rect)
{
rect->x_min = rect->y_min = INT32_MAX;
rect->x_max = rect->y_max = INT32_MIN;
}
static inline void rectangle_update(ASS_Rect *rect,
int32_t x_min, int32_t y_min, int32_t x_max, int32_t y_max)
{
rect->x_min = FFMIN(rect->x_min, x_min);
rect->y_min = FFMIN(rect->y_min, y_min);
rect->x_max = FFMAX(rect->x_max, x_max);
rect->y_max = FFMAX(rect->y_max, y_max);
}
/*
* Outline represented with array of points and array of segments.
* Segment here is spline of order 1 (line), 2 (quadratic) or 3 (cubic).
* Each segment owns number of points equal to its order in point array
* and uses first point owned by the next segment as last point.
* Last segment in each contour instead of the next segment point uses
* point owned by the first segment in that contour. Correspondingly
* total number of points is equal to the sum of spline orders of all segments.
*/
enum {
OUTLINE_LINE_SEGMENT = 1, // line segment
OUTLINE_QUADRATIC_SPLINE = 2, // quadratic spline
OUTLINE_CUBIC_SPLINE = 3, // cubic spline
OUTLINE_COUNT_MASK = 3, // spline order mask
OUTLINE_CONTOUR_END = 4 // last segment in contour flag
};
typedef struct {
size_t n_points, max_points;
size_t n_segments, max_segments;
ASS_Vector *points;
char *segments;
} ASS_Outline;
#define OUTLINE_MIN (-((int32_t) 1 << 28))
#define OUTLINE_MAX (((int32_t) 1 << 28) - 1)
bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_segments);
bool outline_convert(ASS_Outline *outline, const FT_Outline *source);
bool outline_copy(ASS_Outline *outline, const ASS_Outline *source);
void outline_free(ASS_Outline *outline);
bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char segment);
bool outline_add_segment(ASS_Outline *outline, char segment);
bool outline_close_contour(ASS_Outline *outline);
void outline_translate(const ASS_Outline *outline, int32_t dx, int32_t dy);
void outline_adjust(const ASS_Outline *outline, double scale_x, int32_t dx, int32_t dy);
void outline_get_cbox(const ASS_Outline *outline, ASS_Rect *cbox);
bool outline_stroke(ASS_Outline *result, ASS_Outline *result1,
const ASS_Outline *path, int xbord, int ybord, int eps);
#endif /* LIBASS_OUTLINE_H */

1061
libass/ass_parse.c Normal file

File diff suppressed because it is too large Load Diff

40
libass/ass_parse.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_PARSE_H
#define LIBASS_PARSE_H
#define BLUR_MAX_RADIUS 100.0
#define _r(c) ((c) >> 24)
#define _g(c) (((c) >> 16) & 0xFF)
#define _b(c) (((c) >> 8) & 0xFF)
#define _a(c) ((c) & 0xFF)
void update_font(ASS_Renderer *render_priv);
double ensure_font_size(ASS_Renderer *priv, double size);
void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event);
void process_karaoke_effects(ASS_Renderer *render_priv);
unsigned get_next_char(ASS_Renderer *render_priv, char **str);
char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr);
int event_has_hard_overrides(char *str);
extern void change_alpha(uint32_t *var, int32_t new, double pwr);
extern uint32_t mult_alpha(uint32_t a, uint32_t b);
#endif /* LIBASS_PARSE_H */

786
libass/ass_rasterizer.c Normal file
View File

@ -0,0 +1,786 @@
/*
* Copyright (C) 2014 Vabishchevich Nikolay <vabnick@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <assert.h>
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#endif
#include "ass_utils.h"
#include "ass_outline.h"
#include "ass_rasterizer.h"
static inline int ilog2(uint32_t n)
{
#ifdef __GNUC__
return __builtin_clz(n) ^ 31;
#elif defined(_MSC_VER)
int res;
_BitScanReverse(&res, n);
return res;
#else
int res = 0;
for (int ord = 16; ord; ord /= 2)
if (n >= ((uint32_t) 1 << ord)) {
res += ord;
n >>= ord;
}
return res;
#endif
}
bool rasterizer_init(RasterizerData *rst, int tile_order, int outline_error)
{
rst->outline_error = outline_error;
rst->linebuf[0] = rst->linebuf[1] = NULL;
rst->size[0] = rst->capacity[0] = 0;
rst->size[1] = rst->capacity[1] = 0;
rst->n_first = 0;
rst->tile = ass_aligned_alloc(32, 1 << (2 * tile_order), false);
return rst->tile;
}
/**
* \brief Ensure sufficient buffer size (allocate if necessary)
* \param index index (0 or 1) of the input segment buffer (rst->linebuf)
* \param delta requested size increase
* \return false on error
*/
static inline bool check_capacity(RasterizerData *rst, int index, size_t delta)
{
delta += rst->size[index];
if (rst->capacity[index] >= delta)
return true;
size_t capacity = FFMAX(2 * rst->capacity[index], 64);
while (capacity < delta)
capacity *= 2;
void *ptr = realloc(rst->linebuf[index], sizeof(struct segment) * capacity);
if (!ptr)
return false;
rst->linebuf[index] = (struct segment *) ptr;
rst->capacity[index] = capacity;
return true;
}
void rasterizer_done(RasterizerData *rst)
{
free(rst->linebuf[0]);
free(rst->linebuf[1]);
ass_aligned_free(rst->tile);
}
/*
* Tiled Rasterization Algorithm
*
* 1) Convert splines into polylines using recursive subdivision.
*
* 2) Determine which segments of resulting polylines fall into each tile.
* That's done through recursive splitting of segment array with horizontal or vertical lines.
* Each individual segment can lie fully left(top) or right(bottom) from the splitting line or cross it.
* In the latter case copies of such segment go to both output arrays. Also winding count
* of the top-left corner of the second result rectangle gets calculated simultaneously with splitting.
* Winding count of the first result rectangle is the same as of the source rectangle.
*
* 3) When the splitting is done to the tile level, there are 3 possible outcome:
* - Tile doesn't have segments at all--fill it with solid color in accordance with winding count.
* - Tile have only 1 segment--use simple half-plane filling algorithm.
* - Generic case with 2 or more segments.
* In the latter case each segment gets rasterized as right trapezoid into buffer
* with additive or subtractive blending.
*/
// Helper struct for spline split decision
typedef struct {
ASS_Vector r;
int64_t r2, er;
} OutlineSegment;
static inline void segment_init(OutlineSegment *seg,
ASS_Vector beg, ASS_Vector end,
int32_t outline_error)
{
int32_t x = end.x - beg.x;
int32_t y = end.y - beg.y;
int32_t abs_x = x < 0 ? -x : x;
int32_t abs_y = y < 0 ? -y : y;
seg->r.x = x;
seg->r.y = y;
seg->r2 = x * (int64_t) x + y * (int64_t) y;
seg->er = outline_error * (int64_t) FFMAX(abs_x, abs_y);
}
static inline bool segment_subdivide(const OutlineSegment *seg,
ASS_Vector beg, ASS_Vector pt)
{
int32_t x = pt.x - beg.x;
int32_t y = pt.y - beg.y;
int64_t pdr = seg->r.x * (int64_t) x + seg->r.y * (int64_t) y;
int64_t pcr = seg->r.x * (int64_t) y - seg->r.y * (int64_t) x;
return pdr < -seg->er || pdr > seg->r2 + seg->er ||
(pcr < 0 ? -pcr : pcr) > seg->er;
}
/**
* \brief Add new segment to polyline
*/
static bool add_line(RasterizerData *rst, ASS_Vector pt0, ASS_Vector pt1)
{
int32_t x = pt1.x - pt0.x;
int32_t y = pt1.y - pt0.y;
if (!x && !y)
return true;
if (!check_capacity(rst, 0, 1))
return false;
struct segment *line = rst->linebuf[0] + rst->size[0];
rst->size[0]++;
line->flags = SEGFLAG_EXACT_LEFT | SEGFLAG_EXACT_RIGHT |
SEGFLAG_EXACT_TOP | SEGFLAG_EXACT_BOTTOM;
if (x < 0)
line->flags ^= SEGFLAG_UL_DR;
if (y >= 0)
line->flags ^= SEGFLAG_DN | SEGFLAG_UL_DR;
line->x_min = FFMIN(pt0.x, pt1.x);
line->x_max = FFMAX(pt0.x, pt1.x);
line->y_min = FFMIN(pt0.y, pt1.y);
line->y_max = FFMAX(pt0.y, pt1.y);
line->a = y;
line->b = -x;
line->c = y * (int64_t) pt0.x - x * (int64_t) pt0.y;
// halfplane normalization
int32_t abs_x = x < 0 ? -x : x;
int32_t abs_y = y < 0 ? -y : y;
uint32_t max_ab = (abs_x > abs_y ? abs_x : abs_y);
int shift = 30 - ilog2(max_ab);
max_ab <<= shift + 1;
line->a *= 1 << shift;
line->b *= 1 << shift;
line->c *= 1 << shift;
line->scale = (uint64_t) 0x53333333 * (uint32_t) (max_ab * (uint64_t) max_ab >> 32) >> 32;
line->scale += 0x8810624D - (0xBBC6A7EF * (uint64_t) max_ab >> 32);
//line->scale = ((uint64_t) 1 << 61) / max_ab;
return true;
}
/**
* \brief Add quadratic spline to polyline
* Performs recursive subdivision if necessary.
*/
static bool add_quadratic(RasterizerData *rst, const ASS_Vector *pt)
{
OutlineSegment seg;
segment_init(&seg, pt[0], pt[2], rst->outline_error);
if (!segment_subdivide(&seg, pt[0], pt[1]))
return add_line(rst, pt[0], pt[2]);
ASS_Vector next[5];
next[1].x = pt[0].x + pt[1].x;
next[1].y = pt[0].y + pt[1].y;
next[3].x = pt[1].x + pt[2].x;
next[3].y = pt[1].y + pt[2].y;
next[2].x = (next[1].x + next[3].x + 2) >> 2;
next[2].y = (next[1].y + next[3].y + 2) >> 2;
next[1].x >>= 1;
next[1].y >>= 1;
next[3].x >>= 1;
next[3].y >>= 1;
next[0] = pt[0];
next[4] = pt[2];
return add_quadratic(rst, next) && add_quadratic(rst, next + 2);
}
/**
* \brief Add cubic spline to polyline
* Performs recursive subdivision if necessary.
*/
static bool add_cubic(RasterizerData *rst, const ASS_Vector *pt)
{
OutlineSegment seg;
segment_init(&seg, pt[0], pt[3], rst->outline_error);
if (!segment_subdivide(&seg, pt[0], pt[1]) && !segment_subdivide(&seg, pt[0], pt[2]))
return add_line(rst, pt[0], pt[3]);
ASS_Vector next[7], center;
next[1].x = pt[0].x + pt[1].x;
next[1].y = pt[0].y + pt[1].y;
center.x = pt[1].x + pt[2].x + 2;
center.y = pt[1].y + pt[2].y + 2;
next[5].x = pt[2].x + pt[3].x;
next[5].y = pt[2].y + pt[3].y;
next[2].x = next[1].x + center.x;
next[2].y = next[1].y + center.y;
next[4].x = center.x + next[5].x;
next[4].y = center.y + next[5].y;
next[3].x = (next[2].x + next[4].x - 1) >> 3;
next[3].y = (next[2].y + next[4].y - 1) >> 3;
next[2].x >>= 2;
next[2].y >>= 2;
next[4].x >>= 2;
next[4].y >>= 2;
next[1].x >>= 1;
next[1].y >>= 1;
next[5].x >>= 1;
next[5].y >>= 1;
next[0] = pt[0];
next[6] = pt[3];
return add_cubic(rst, next) && add_cubic(rst, next + 3);
}
bool rasterizer_set_outline(RasterizerData *rst,
const ASS_Outline *path, bool extra)
{
if (!extra) {
rectangle_reset(&rst->bbox);
rst->n_first = 0;
}
rst->size[0] = rst->n_first;
for (size_t i = 0; i < path->n_points; i++) {
if (path->points[i].x < OUTLINE_MIN || path->points[i].x > OUTLINE_MAX)
return false;
if (path->points[i].y < OUTLINE_MIN || path->points[i].y > OUTLINE_MAX)
return false;
}
ASS_Vector *start = path->points, *cur = start;
for (size_t i = 0; i < path->n_segments; i++) {
int n = path->segments[i] & OUTLINE_COUNT_MASK;
cur += n;
ASS_Vector *end = cur, p[4];
if (path->segments[i] & OUTLINE_CONTOUR_END) {
end = start;
start = cur;
}
switch (n) {
case OUTLINE_LINE_SEGMENT:
if (!add_line(rst, cur[-1], *end))
return false;
break;
case OUTLINE_QUADRATIC_SPLINE:
p[0] = cur[-2];
p[1] = cur[-1];
p[2] = *end;
if (!add_quadratic(rst, p))
return false;
break;
case OUTLINE_CUBIC_SPLINE:
p[0] = cur[-3];
p[1] = cur[-2];
p[2] = cur[-1];
p[3] = *end;
if (!add_cubic(rst, p))
return false;
break;
default:
return false;
}
}
assert(start == cur && cur == path->points + path->n_points);
for (size_t k = rst->n_first; k < rst->size[0]; k++) {
struct segment *line = &rst->linebuf[0][k];
rectangle_update(&rst->bbox,
line->x_min, line->y_min,
line->x_max, line->y_max);
}
if (!extra)
rst->n_first = rst->size[0];
return true;
}
static void segment_move_x(struct segment *line, int32_t x)
{
line->x_min -= x;
line->x_max -= x;
line->x_min = FFMAX(line->x_min, 0);
line->c -= line->a * (int64_t) x;
static const int test = SEGFLAG_EXACT_LEFT | SEGFLAG_UL_DR;
if (!line->x_min && (line->flags & test) == test)
line->flags &= ~SEGFLAG_EXACT_TOP;
}
static void segment_move_y(struct segment *line, int32_t y)
{
line->y_min -= y;
line->y_max -= y;
line->y_min = FFMAX(line->y_min, 0);
line->c -= line->b * (int64_t) y;
static const int test = SEGFLAG_EXACT_TOP | SEGFLAG_UL_DR;
if (!line->y_min && (line->flags & test) == test)
line->flags &= ~SEGFLAG_EXACT_LEFT;
}
static void segment_split_horz(struct segment *line, struct segment *next, int32_t x)
{
assert(x > line->x_min && x < line->x_max);
*next = *line;
next->c -= line->a * (int64_t) x;
next->x_min = 0;
next->x_max -= x;
line->x_max = x;
line->flags &= ~SEGFLAG_EXACT_TOP;
next->flags &= ~SEGFLAG_EXACT_BOTTOM;
if (line->flags & SEGFLAG_UL_DR) {
int32_t tmp = line->flags;
line->flags = next->flags;
next->flags = tmp;
}
line->flags |= SEGFLAG_EXACT_RIGHT;
next->flags |= SEGFLAG_EXACT_LEFT;
}
static void segment_split_vert(struct segment *line, struct segment *next, int32_t y)
{
assert(y > line->y_min && y < line->y_max);
*next = *line;
next->c -= line->b * (int64_t) y;
next->y_min = 0;
next->y_max -= y;
line->y_max = y;
line->flags &= ~SEGFLAG_EXACT_LEFT;
next->flags &= ~SEGFLAG_EXACT_RIGHT;
if (line->flags & SEGFLAG_UL_DR) {
int32_t tmp = line->flags;
line->flags = next->flags;
next->flags = tmp;
}
line->flags |= SEGFLAG_EXACT_BOTTOM;
next->flags |= SEGFLAG_EXACT_TOP;
}
static inline int segment_check_left(const struct segment *line, int32_t x)
{
if (line->flags & SEGFLAG_EXACT_LEFT)
return line->x_min >= x;
int64_t cc = line->c - line->a * (int64_t) x -
line->b * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->y_min : line->y_max);
if (line->a < 0)
cc = -cc;
return cc >= 0;
}
static inline int segment_check_right(const struct segment *line, int32_t x)
{
if (line->flags & SEGFLAG_EXACT_RIGHT)
return line->x_max <= x;
int64_t cc = line->c - line->a * (int64_t) x -
line->b * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->y_max : line->y_min);
if (line->a > 0)
cc = -cc;
return cc >= 0;
}
static inline int segment_check_top(const struct segment *line, int32_t y)
{
if (line->flags & SEGFLAG_EXACT_TOP)
return line->y_min >= y;
int64_t cc = line->c - line->b * (int64_t) y -
line->a * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->x_min : line->x_max);
if (line->b < 0)
cc = -cc;
return cc >= 0;
}
static inline int segment_check_bottom(const struct segment *line, int32_t y)
{
if (line->flags & SEGFLAG_EXACT_BOTTOM)
return line->y_max <= y;
int64_t cc = line->c - line->b * (int64_t) y -
line->a * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->x_max : line->x_min);
if (line->b > 0)
cc = -cc;
return cc >= 0;
}
/**
* \brief Split list of segments horizontally
* \param src in: input array, can coincide with *dst0 or *dst1
* \param n_src in: numbers of input segments for both groups
* \param dst0, dst1 out: output arrays of at least n_src[0] + n_src[1] size
* \param n_dst0, n_dst1 out: numbers of output segments for both groups
* \param winding out: resulting winding of bottom-split point
* \param x in: split coordinate
*/
static void polyline_split_horz(const struct segment *src, const size_t n_src[2],
struct segment *dst0, size_t n_dst0[2],
struct segment *dst1, size_t n_dst1[2],
int winding[2], int32_t x)
{
const struct segment *cmp = src + n_src[0];
const struct segment *end = cmp + n_src[1];
n_dst0[0] = n_dst0[1] = 0;
n_dst1[0] = n_dst1[1] = 0;
for (; src != end; src++) {
int group = src < cmp ? 0 : 1;
int delta = 0;
if (!src->y_min && (src->flags & SEGFLAG_EXACT_TOP))
delta = src->a < 0 ? 1 : -1;
if (segment_check_right(src, x)) {
winding[group] += delta;
if (src->x_min >= x)
continue;
*dst0 = *src;
dst0->x_max = FFMIN(dst0->x_max, x);
n_dst0[group]++;
dst0++;
continue;
}
if (segment_check_left(src, x)) {
*dst1 = *src;
segment_move_x(dst1, x);
n_dst1[group]++;
dst1++;
continue;
}
if (src->flags & SEGFLAG_UL_DR)
winding[group] += delta;
*dst0 = *src;
segment_split_horz(dst0, dst1, x);
n_dst0[group]++;
dst0++;
n_dst1[group]++;
dst1++;
}
}
/**
* \brief Split list of segments vertically
*/
static void polyline_split_vert(const struct segment *src, const size_t n_src[2],
struct segment *dst0, size_t n_dst0[2],
struct segment *dst1, size_t n_dst1[2],
int winding[2], int32_t y)
{
const struct segment *cmp = src + n_src[0];
const struct segment *end = cmp + n_src[1];
n_dst0[0] = n_dst0[1] = 0;
n_dst1[0] = n_dst1[1] = 0;
for (; src != end; src++) {
int group = src < cmp ? 0 : 1;
int delta = 0;
if (!src->x_min && (src->flags & SEGFLAG_EXACT_LEFT))
delta = src->b < 0 ? 1 : -1;
if (segment_check_bottom(src, y)) {
winding[group] += delta;
if (src->y_min >= y)
continue;
*dst0 = *src;
dst0->y_max = dst0->y_max < y ? dst0->y_max : y;
n_dst0[group]++;
dst0++;
continue;
}
if (segment_check_top(src, y)) {
*dst1 = *src;
segment_move_y(dst1, y);
n_dst1[group]++;
dst1++;
continue;
}
if (src->flags & SEGFLAG_UL_DR)
winding[group] += delta;
*dst0 = *src;
segment_split_vert(dst0, dst1, y);
n_dst0[group]++;
dst0++;
n_dst1[group]++;
dst1++;
}
}
static inline void rasterizer_fill_solid(const BitmapEngine *engine,
uint8_t *buf, int width, int height, ptrdiff_t stride,
int set)
{
assert(!(width & ((1 << engine->tile_order) - 1)));
assert(!(height & ((1 << engine->tile_order) - 1)));
ptrdiff_t step = 1 << engine->tile_order;
ptrdiff_t tile_stride = stride * (1 << engine->tile_order);
width >>= engine->tile_order;
height >>= engine->tile_order;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
engine->fill_solid(buf + x * step, stride, set);
buf += tile_stride;
}
}
static inline void rasterizer_fill_halfplane(const BitmapEngine *engine,
uint8_t *buf, int width, int height, ptrdiff_t stride,
int32_t a, int32_t b, int64_t c, int32_t scale)
{
assert(!(width & ((1 << engine->tile_order) - 1)));
assert(!(height & ((1 << engine->tile_order) - 1)));
if (width == 1 << engine->tile_order && height == 1 << engine->tile_order) {
engine->fill_halfplane(buf, stride, a, b, c, scale);
return;
}
uint32_t abs_a = a < 0 ? -a : a;
uint32_t abs_b = b < 0 ? -b : b;
int64_t size = (int64_t) (abs_a + abs_b) << (engine->tile_order + 5);
int64_t offs = ((int64_t) a + b) * (1 << (engine->tile_order + 5));
ptrdiff_t step = 1 << engine->tile_order;
ptrdiff_t tile_stride = stride * (1 << engine->tile_order);
width >>= engine->tile_order;
height >>= engine->tile_order;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int64_t cc = c - (a * (int64_t) x + b * (int64_t) y) * (1 << (engine->tile_order + 6));
int64_t offs_c = offs - cc;
int64_t abs_c = offs_c < 0 ? -offs_c : offs_c;
if (abs_c < size)
engine->fill_halfplane(buf + x * step, stride, a, b, cc, scale);
else
engine->fill_solid(buf + x * step, stride,
((uint32_t) (offs_c >> 32) ^ scale) & 0x80000000);
}
buf += tile_stride;
}
}
enum {
FLAG_SOLID = 1,
FLAG_COMPLEX = 2,
FLAG_REVERSE = 4,
FLAG_GENERIC = 8,
};
static inline int get_fill_flags(struct segment *line, size_t n_lines, int winding)
{
if (!n_lines)
return winding ? FLAG_SOLID : 0;
if (n_lines > 1)
return FLAG_COMPLEX | FLAG_GENERIC;
static const int test = SEGFLAG_UL_DR | SEGFLAG_EXACT_LEFT;
if (((line->flags & test) != test) == !(line->flags & SEGFLAG_DN))
winding++;
switch (winding) {
case 0:
return FLAG_COMPLEX | FLAG_REVERSE;
case 1:
return FLAG_COMPLEX;
default:
return FLAG_SOLID;
}
}
/**
* \brief Main quad-tree filling function
* \param index index (0 or 1) of the input segment buffer (rst->linebuf)
* \param offs current offset from the beginning of the buffer
* \param winding bottom-left winding value
* \return false on error
* Rasterizes (possibly recursive) one quad-tree level.
* Truncates used input buffer.
*/
static bool rasterizer_fill_level(const BitmapEngine *engine, RasterizerData *rst,
uint8_t *buf, int width, int height, ptrdiff_t stride,
int index, const size_t n_lines[2], const int winding[2])
{
assert(width > 0 && height > 0);
assert((unsigned) index < 2u && n_lines[0] + n_lines[1] <= rst->size[index]);
assert(!(width & ((1 << engine->tile_order) - 1)));
assert(!(height & ((1 << engine->tile_order) - 1)));
size_t offs = rst->size[index] - n_lines[0] - n_lines[1];
struct segment *line = rst->linebuf[index] + offs, *line1 = line + n_lines[0];
int flags0 = get_fill_flags(line, n_lines[0], winding[0]);
int flags1 = get_fill_flags(line1, n_lines[1], winding[1]);
int flags = (flags0 | flags1) ^ FLAG_COMPLEX;
if (flags & (FLAG_SOLID | FLAG_COMPLEX)) {
rasterizer_fill_solid(engine, buf, width, height, stride, flags & FLAG_SOLID);
rst->size[index] = offs;
return true;
}
if (!(flags & FLAG_GENERIC) && ((flags0 ^ flags1) & FLAG_COMPLEX)) {
if (flags1 & FLAG_COMPLEX)
line = line1;
rasterizer_fill_halfplane(engine, buf, width, height, stride,
line->a, line->b, line->c,
flags & FLAG_REVERSE ? -line->scale : line->scale);
rst->size[index] = offs;
return true;
}
if (width == 1 << engine->tile_order && height == 1 << engine->tile_order) {
if (!(flags1 & FLAG_COMPLEX)) {
engine->fill_generic(buf, stride, line, n_lines[0], winding[0]);
rst->size[index] = offs;
return true;
}
if (!(flags0 & FLAG_COMPLEX)) {
engine->fill_generic(buf, stride, line1, n_lines[1], winding[1]);
rst->size[index] = offs;
return true;
}
if (flags0 & FLAG_GENERIC)
engine->fill_generic(buf, stride, line, n_lines[0], winding[0]);
else
engine->fill_halfplane(buf, stride, line->a, line->b, line->c,
flags0 & FLAG_REVERSE ? -line->scale : line->scale);
if (flags1 & FLAG_GENERIC)
engine->fill_generic(rst->tile, width, line1, n_lines[1], winding[1]);
else
engine->fill_halfplane(rst->tile, width, line1->a, line1->b, line1->c,
flags1 & FLAG_REVERSE ? -line1->scale : line1->scale);
// XXX: better to use max instead of add
engine->add_bitmaps(buf, stride, rst->tile, width, height, width);
rst->size[index] = offs;
return true;
}
size_t offs1 = rst->size[index ^ 1];
if (!check_capacity(rst, index ^ 1, n_lines[0] + n_lines[1]))
return false;
struct segment *dst0 = line;
struct segment *dst1 = rst->linebuf[index ^ 1] + offs1;
uint8_t *buf1 = buf;
int width1 = width;
int height1 = height;
size_t n_next0[2], n_next1[2];
int winding1[2] = { winding[0], winding[1] };
if (width > height) {
width = 1 << ilog2(width - 1);
width1 -= width;
buf1 += width;
polyline_split_horz(line, n_lines,
dst0, n_next0, dst1, n_next1,
winding1, (int32_t) width << 6);
} else {
height = 1 << ilog2(height - 1);
height1 -= height;
buf1 += height * stride;
polyline_split_vert(line, n_lines,
dst0, n_next0, dst1, n_next1,
winding1, (int32_t) height << 6);
}
rst->size[index ^ 0] = offs + n_next0[0] + n_next0[1];
rst->size[index ^ 1] = offs1 + n_next1[0] + n_next1[1];
if (!rasterizer_fill_level(engine, rst, buf, width, height, stride, index ^ 0, n_next0, winding))
return false;
assert(rst->size[index ^ 0] == offs);
if (!rasterizer_fill_level(engine, rst, buf1, width1, height1, stride, index ^ 1, n_next1, winding1))
return false;
assert(rst->size[index ^ 1] == offs1);
return true;
}
bool rasterizer_fill(const BitmapEngine *engine, RasterizerData *rst,
uint8_t *buf, int x0, int y0,
int width, int height, ptrdiff_t stride)
{
assert(width > 0 && height > 0);
assert(!(width & ((1 << engine->tile_order) - 1)));
assert(!(height & ((1 << engine->tile_order) - 1)));
x0 *= 1 << 6; y0 *= 1 << 6;
struct segment *line = rst->linebuf[0];
struct segment *end = line + rst->size[0];
for (; line != end; line++) {
line->x_min -= x0;
line->x_max -= x0;
line->y_min -= y0;
line->y_max -= y0;
line->c -= line->a * (int64_t) x0 + line->b * (int64_t) y0;
}
rst->bbox.x_min -= x0;
rst->bbox.x_max -= x0;
rst->bbox.y_min -= y0;
rst->bbox.y_max -= y0;
if (!check_capacity(rst, 1, rst->size[0]))
return false;
size_t n_unused[2];
size_t n_lines[2] = { rst->n_first, rst->size[0] - rst->n_first };
int winding[2] = { 0, 0 };
int32_t size_x = (int32_t) width << 6;
int32_t size_y = (int32_t) height << 6;
if (rst->bbox.x_max >= size_x) {
polyline_split_horz(rst->linebuf[0], n_lines,
rst->linebuf[0], n_lines,
rst->linebuf[1], n_unused,
winding, size_x);
winding[0] = winding[1] = 0;
}
if (rst->bbox.y_max >= size_y) {
polyline_split_vert(rst->linebuf[0], n_lines,
rst->linebuf[0], n_lines,
rst->linebuf[1], n_unused,
winding, size_y);
winding[0] = winding[1] = 0;
}
if (rst->bbox.x_min <= 0) {
polyline_split_horz(rst->linebuf[0], n_lines,
rst->linebuf[1], n_unused,
rst->linebuf[0], n_lines,
winding, 0);
}
if (rst->bbox.y_min <= 0) {
polyline_split_vert(rst->linebuf[0], n_lines,
rst->linebuf[1], n_unused,
rst->linebuf[0], n_lines,
winding, 0);
}
rst->size[0] = n_lines[0] + n_lines[1];
rst->size[1] = 0;
return rasterizer_fill_level(engine, rst,
buf, width, height, stride,
0, n_lines, winding);
}

84
libass/ass_rasterizer.h Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2014 Vabishchevich Nikolay <vabnick@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_RASTERIZER_H
#define LIBASS_RASTERIZER_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "ass_bitmap.h"
enum {
SEGFLAG_DN = 1,
SEGFLAG_UL_DR = 2,
SEGFLAG_EXACT_LEFT = 4,
SEGFLAG_EXACT_RIGHT = 8,
SEGFLAG_EXACT_TOP = 16,
SEGFLAG_EXACT_BOTTOM = 32,
};
// Polyline segment struct
struct segment {
int64_t c;
int32_t a, b, scale, flags;
int32_t x_min, x_max, y_min, y_max;
};
typedef struct {
int outline_error; // acceptable error (in 1/64 pixel units)
// usable after rasterizer_set_outline
ASS_Rect bbox;
// internal buffers
struct segment *linebuf[2];
size_t size[2], capacity[2];
size_t n_first;
uint8_t *tile;
} RasterizerData;
bool rasterizer_init(RasterizerData *rst, int tile_order, int outline_error);
void rasterizer_done(RasterizerData *rst);
/**
* \brief Convert outline to polyline and calculate exact bounds
* \param path in: source outline
* \param extra in: true if path is second border outline
* \return false on error
*/
bool rasterizer_set_outline(RasterizerData *rst,
const ASS_Outline *path, bool extra);
/**
* \brief Polyline rasterization function
* \param x0, y0, width, height in: source window (full pixel units)
* \param buf out: aligned output buffer (size = stride * height)
* \param stride output buffer stride (aligned)
* \return false on error
* Deletes preprocessed polyline after work.
*/
bool rasterizer_fill(const BitmapEngine *engine, RasterizerData *rst,
uint8_t *buf, int x0, int y0,
int width, int height, ptrdiff_t stride);
#endif /* LIBASS_RASTERIZER_H */

377
libass/ass_rasterizer_c.c Normal file
View File

@ -0,0 +1,377 @@
/*
* Copyright (C) 2014 Vabishchevich Nikolay <vabnick@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include "ass_utils.h"
#include "ass_rasterizer.h"
#include <assert.h>
void ass_fill_solid_tile16_c(uint8_t *buf, ptrdiff_t stride, int set)
{
uint8_t value = set ? 255 : 0;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++)
buf[x] = value;
buf += stride;
}
}
void ass_fill_solid_tile32_c(uint8_t *buf, ptrdiff_t stride, int set)
{
uint8_t value = set ? 255 : 0;
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 32; x++)
buf[x] = value;
buf += stride;
}
}
/*
* Halfplane Filling Functions
*
* Fill pixels with antialiasing corresponding to equation
* A * x + B * y < C, where
* x, y - offset of pixel center from bottom-left,
* A = a * scale, B = b * scale, C = c * scale / 64.
*
* Normalization of coefficients prior call:
* max(abs(a), abs(b)) * scale = 1 << 61
*
* Used Algorithm
* Let
* max_ab = max(abs(A), abs(B)),
* min_ab = min(abs(A), abs(B)),
* CC = C - A * x - B * y, then
* result = (clamp((CC - min_ab / 4) / max_ab) +
* clamp((CC + min_ab / 4) / max_ab) +
* 1) / 2,
* where clamp(Z) = max(-0.5, min(0.5, Z)).
*/
void ass_fill_halfplane_tile16_c(uint8_t *buf, ptrdiff_t stride,
int32_t a, int32_t b, int64_t c, int32_t scale)
{
int16_t aa = (a * (int64_t) scale + ((int64_t) 1 << 49)) >> 50;
int16_t bb = (b * (int64_t) scale + ((int64_t) 1 << 49)) >> 50;
int16_t cc = ((int32_t) (c >> 11) * (int64_t) scale + ((int64_t) 1 << 44)) >> 45;
cc += (1 << 9) - ((aa + bb) >> 1);
int16_t abs_a = aa < 0 ? -aa : aa;
int16_t abs_b = bb < 0 ? -bb : bb;
int16_t delta = (FFMIN(abs_a, abs_b) + 2) >> 2;
int16_t va1[16], va2[16];
for (int x = 0; x < 16; x++) {
va1[x] = aa * x - delta;
va2[x] = aa * x + delta;
}
static const int16_t full = (1 << 10) - 1;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
int16_t c1 = cc - va1[x];
int16_t c2 = cc - va2[x];
c1 = FFMINMAX(c1, 0, full);
c2 = FFMINMAX(c2, 0, full);
buf[x] = (c1 + c2) >> 3;
}
buf += stride;
cc -= bb;
}
}
void ass_fill_halfplane_tile32_c(uint8_t *buf, ptrdiff_t stride,
int32_t a, int32_t b, int64_t c, int32_t scale)
{
int16_t aa = (a * (int64_t) scale + ((int64_t) 1 << 50)) >> 51;
int16_t bb = (b * (int64_t) scale + ((int64_t) 1 << 50)) >> 51;
int16_t cc = ((int32_t) (c >> 12) * (int64_t) scale + ((int64_t) 1 << 44)) >> 45;
cc += (1 << 8) - ((aa + bb) >> 1);
int16_t abs_a = aa < 0 ? -aa : aa;
int16_t abs_b = bb < 0 ? -bb : bb;
int16_t delta = (FFMIN(abs_a, abs_b) + 2) >> 2;
int16_t va1[32], va2[32];
for (int x = 0; x < 32; x++) {
va1[x] = aa * x - delta;
va2[x] = aa * x + delta;
}
static const int16_t full = (1 << 9) - 1;
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 32; x++) {
int16_t c1 = cc - va1[x];
int16_t c2 = cc - va2[x];
c1 = FFMINMAX(c1, 0, full);
c2 = FFMINMAX(c2, 0, full);
buf[x] = (c1 + c2) >> 2;
}
buf += stride;
cc -= bb;
}
}
/*
* Generic Filling Functions
*
* Used Algorithm
* Construct trapeziod from each polyline segment and its projection into left side of tile.
* Render that trapeziod into internal buffer with additive blending and correct sign.
* Store clamped absolute value from internal buffer into result buffer.
*/
// Render top/bottom line of the trapeziod with antialiasing
static inline void update_border_line16(int16_t res[16],
int16_t abs_a, const int16_t va[16],
int16_t b, int16_t abs_b,
int16_t c, int up, int dn)
{
int16_t size = dn - up;
int16_t w = (1 << 10) + (size << 4) - abs_a;
w = FFMIN(w, 1 << 10) << 3;
int16_t dc_b = abs_b * (int32_t) size >> 6;
int16_t dc = (FFMIN(abs_a, dc_b) + 2) >> 2;
int16_t base = (int32_t) b * (int16_t) (up + dn) >> 7;
int16_t offs1 = size - ((base + dc) * (int32_t) w >> 16);
int16_t offs2 = size - ((base - dc) * (int32_t) w >> 16);
size <<= 1;
for (int x = 0; x < 16; x++) {
int16_t cw = (c - va[x]) * (int32_t) w >> 16;
int16_t c1 = cw + offs1;
int16_t c2 = cw + offs2;
c1 = FFMINMAX(c1, 0, size);
c2 = FFMINMAX(c2, 0, size);
res[x] += c1 + c2;
}
}
void ass_fill_generic_tile16_c(uint8_t *buf, ptrdiff_t stride,
const struct segment *line, size_t n_lines,
int winding)
{
int16_t res[16][16], delta[18];
for (int y = 0; y < 16; y++)
for (int x = 0; x < 16; x++)
res[y][x] = 0;
for (int y = 0; y < 18; y++)
delta[y] = 0;
static const int16_t full = 1 << 10;
const struct segment *end = line + n_lines;
for (; line != end; ++line) {
assert(line->y_min >= 0 && line->y_min < 1 << 10);
assert(line->y_max > 0 && line->y_max <= 1 << 10);
assert(line->y_min <= line->y_max);
int16_t up_delta = line->flags & SEGFLAG_DN ? 4 : 0;
int16_t dn_delta = up_delta;
if (!line->x_min && (line->flags & SEGFLAG_EXACT_LEFT)) dn_delta ^= 4;
if (line->flags & SEGFLAG_UL_DR) {
int16_t tmp = up_delta;
up_delta = dn_delta;
dn_delta = tmp;
}
int up = line->y_min >> 6, dn = line->y_max >> 6;
int16_t up_pos = line->y_min & 63;
int16_t up_delta1 = up_delta * up_pos;
int16_t dn_pos = line->y_max & 63;
int16_t dn_delta1 = dn_delta * dn_pos;
delta[up + 1] -= up_delta1;
delta[up] -= (up_delta << 6) - up_delta1;
delta[dn + 1] += dn_delta1;
delta[dn] += (dn_delta << 6) - dn_delta1;
if (line->y_min == line->y_max)
continue;
int16_t a = (line->a * (int64_t) line->scale + ((int64_t) 1 << 49)) >> 50;
int16_t b = (line->b * (int64_t) line->scale + ((int64_t) 1 << 49)) >> 50;
int16_t c = ((int32_t) (line->c >> 11) * (int64_t) line->scale + ((int64_t) 1 << 44)) >> 45;
c -= (a >> 1) + b * up;
int16_t va[16];
for (int x = 0; x < 16; x++)
va[x] = a * x;
int16_t abs_a = a < 0 ? -a : a;
int16_t abs_b = b < 0 ? -b : b;
int16_t dc = (FFMIN(abs_a, abs_b) + 2) >> 2;
int16_t base = (1 << 9) - (b >> 1);
int16_t dc1 = base + dc;
int16_t dc2 = base - dc;
if (up_pos) {
if (dn == up) {
update_border_line16(res[up], abs_a, va, b, abs_b, c, up_pos, dn_pos);
continue;
}
update_border_line16(res[up], abs_a, va, b, abs_b, c, up_pos, 64);
up++;
c -= b;
}
for (int y = up; y < dn; y++) {
for (int x = 0; x < 16; x++) {
int16_t c1 = c - va[x] + dc1;
int16_t c2 = c - va[x] + dc2;
c1 = FFMINMAX(c1, 0, full);
c2 = FFMINMAX(c2, 0, full);
res[y][x] += (c1 + c2) >> 3;
}
c -= b;
}
if (dn_pos)
update_border_line16(res[dn], abs_a, va, b, abs_b, c, 0, dn_pos);
}
int16_t cur = 256 * winding;
for (int y = 0; y < 16; y++) {
cur += delta[y];
for (int x = 0; x < 16; x++) {
int16_t val = res[y][x] + cur, neg_val = -val;
val = (val > neg_val ? val : neg_val);
buf[x] = FFMIN(val, 255);
}
buf += stride;
}
}
// Render top/bottom line of the trapeziod with antialiasing
static inline void update_border_line32(int16_t res[32],
int16_t abs_a, const int16_t va[32],
int16_t b, int16_t abs_b,
int16_t c, int up, int dn)
{
int16_t size = dn - up;
int16_t w = (1 << 9) + (size << 3) - abs_a;
w = FFMIN(w, 1 << 9) << 5;
int16_t dc_b = abs_b * (int32_t) size >> 6;
int16_t dc = (FFMIN(abs_a, dc_b) + 2) >> 2;
int16_t base = (int32_t) b * (int16_t) (up + dn) >> 7;
int16_t offs1 = size - ((base + dc) * (int32_t) w >> 16);
int16_t offs2 = size - ((base - dc) * (int32_t) w >> 16);
size <<= 1;
for (int x = 0; x < 32; x++) {
int16_t cw = (c - va[x]) * (int32_t) w >> 16;
int16_t c1 = cw + offs1;
int16_t c2 = cw + offs2;
c1 = FFMINMAX(c1, 0, size);
c2 = FFMINMAX(c2, 0, size);
res[x] += c1 + c2;
}
}
void ass_fill_generic_tile32_c(uint8_t *buf, ptrdiff_t stride,
const struct segment *line, size_t n_lines,
int winding)
{
int16_t res[32][32], delta[34];
for (int y = 0; y < 32; y++)
for (int x = 0; x < 32; x++)
res[y][x] = 0;
for (int y = 0; y < 34; y++)
delta[y] = 0;
static const int16_t full = 1 << 9;
const struct segment *end = line + n_lines;
for (; line != end; ++line) {
assert(line->y_min >= 0 && line->y_min < 1 << 11);
assert(line->y_max > 0 && line->y_max <= 1 << 11);
assert(line->y_min <= line->y_max);
int16_t up_delta = line->flags & SEGFLAG_DN ? 4 : 0;
int16_t dn_delta = up_delta;
if (!line->x_min && (line->flags & SEGFLAG_EXACT_LEFT)) dn_delta ^= 4;
if (line->flags & SEGFLAG_UL_DR) {
int16_t tmp = up_delta;
up_delta = dn_delta;
dn_delta = tmp;
}
int up = line->y_min >> 6, dn = line->y_max >> 6;
int16_t up_pos = line->y_min & 63;
int16_t up_delta1 = up_delta * up_pos;
int16_t dn_pos = line->y_max & 63;
int16_t dn_delta1 = dn_delta * dn_pos;
delta[up + 1] -= up_delta1;
delta[up] -= (up_delta << 6) - up_delta1;
delta[dn + 1] += dn_delta1;
delta[dn] += (dn_delta << 6) - dn_delta1;
if (line->y_min == line->y_max)
continue;
int16_t a = (line->a * (int64_t) line->scale + ((int64_t) 1 << 50)) >> 51;
int16_t b = (line->b * (int64_t) line->scale + ((int64_t) 1 << 50)) >> 51;
int16_t c = ((int32_t) (line->c >> 12) * (int64_t) line->scale + ((int64_t) 1 << 44)) >> 45;
c -= (a >> 1) + b * up;
int16_t va[32];
for (int x = 0; x < 32; x++)
va[x] = a * x;
int16_t abs_a = a < 0 ? -a : a;
int16_t abs_b = b < 0 ? -b : b;
int16_t dc = (FFMIN(abs_a, abs_b) + 2) >> 2;
int16_t base = (1 << 8) - (b >> 1);
int16_t dc1 = base + dc;
int16_t dc2 = base - dc;
if (up_pos) {
if (dn == up) {
update_border_line32(res[up], abs_a, va, b, abs_b, c, up_pos, dn_pos);
continue;
}
update_border_line32(res[up], abs_a, va, b, abs_b, c, up_pos, 64);
up++;
c -= b;
}
for (int y = up; y < dn; y++) {
for (int x = 0; x < 32; x++) {
int16_t c1 = c - va[x] + dc1;
int16_t c2 = c - va[x] + dc2;
c1 = FFMINMAX(c1, 0, full);
c2 = FFMINMAX(c2, 0, full);
res[y][x] += (c1 + c2) >> 2;
}
c -= b;
}
if (dn_pos)
update_border_line32(res[dn], abs_a, va, b, abs_b, c, 0, dn_pos);
}
int16_t cur = 256 * winding;
for (int y = 0; y < 32; y++) {
cur += delta[y];
for (int x = 0; x < 32; x++) {
int16_t val = res[y][x] + cur, neg_val = -val;
val = (val > neg_val ? val : neg_val);
buf[x] = FFMIN(val, 255);
}
buf += stride;
}
}

2945
libass/ass_render.c Normal file

File diff suppressed because it is too large Load Diff

339
libass/ass_render.h Normal file
View File

@ -0,0 +1,339 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_RENDER_H
#define LIBASS_RENDER_H
#include <inttypes.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_SYNTHESIS_H
#ifdef CONFIG_HARFBUZZ
#include <hb.h>
#endif
#include "ass.h"
#include "ass_font.h"
#include "ass_bitmap.h"
#include "ass_cache.h"
#include "ass_utils.h"
#include "ass_fontselect.h"
#include "ass_library.h"
#include "ass_drawing.h"
#include "ass_bitmap.h"
#include "ass_rasterizer.h"
#define GLYPH_CACHE_MAX 10000
#define MEGABYTE (1024 * 1024)
#define BITMAP_CACHE_MAX_SIZE (128 * MEGABYTE)
#define COMPOSITE_CACHE_RATIO 2
#define COMPOSITE_CACHE_MAX_SIZE (BITMAP_CACHE_MAX_SIZE / COMPOSITE_CACHE_RATIO)
#define PARSED_FADE (1<<0)
#define PARSED_A (1<<1)
typedef struct {
ASS_Image result;
CompositeHashValue *source;
size_t ref_count;
} ASS_ImagePriv;
typedef struct {
int frame_width;
int frame_height;
int storage_width; // video width before any rescaling
int storage_height; // video height before any rescaling
double font_size_coeff; // font size multiplier
double line_spacing; // additional line spacing (in frame pixels)
double line_position; // vertical position for subtitles, 0-100 (0 = no change)
int top_margin; // height of top margin. Everything except toptitles is shifted down by top_margin.
int bottom_margin; // height of bottom margin. (frame_height - top_margin - bottom_margin) is original video height.
int left_margin;
int right_margin;
int use_margins; // 0 - place all subtitles inside original frame
// 1 - use margins for placing toptitles and subtitles
double par; // user defined pixel aspect ratio (0 = unset)
ASS_Hinting hinting;
ASS_ShapingLevel shaper;
int selective_style_overrides; // ASS_OVERRIDE_* flags
char *default_font;
char *default_family;
} ASS_Settings;
// a rendered event
typedef struct {
ASS_Image *imgs;
int top, height, left, width;
int detect_collisions;
int shift_direction;
ASS_Event *event;
} EventImages;
typedef enum {
EF_NONE = 0,
EF_KARAOKE,
EF_KARAOKE_KF,
EF_KARAOKE_KO
} Effect;
// describes a combined bitmap
typedef struct {
FilterDesc filter;
uint32_t c[4]; // colors
Effect effect_type;
int effect_timing; // time duration of current karaoke word
// after process_karaoke_effects: distance in pixels from the glyph origin.
// part of the glyph to the left of it is displayed in a different color.
int first_pos_x;
size_t bitmap_count, max_bitmap_count;
BitmapRef *bitmaps;
int x, y;
ASS_Rect rect, rect_o;
size_t n_bm, n_bm_o;
Bitmap *bm, *bm_o, *bm_s; // glyphs, outline, shadow bitmaps
CompositeHashValue *image;
} CombinedBitmapInfo;
// describes a glyph
// GlyphInfo and TextInfo are used for text centering and word-wrapping operations
typedef struct glyph_info {
unsigned symbol;
unsigned skip; // skip glyph when layouting text
ASS_Font *font;
int face_index;
int glyph_index;
#ifdef CONFIG_HARFBUZZ
hb_script_t script;
#else
int script;
#endif
double font_size;
ASS_Drawing *drawing;
ASS_Outline *outline;
ASS_Outline *border[2];
ASS_Rect bbox;
ASS_Vector pos;
ASS_Vector offset;
char linebreak; // the first (leading) glyph of some line ?
uint32_t c[4]; // colors
ASS_Vector advance; // 26.6
ASS_Vector cluster_advance;
char effect; // the first (leading) glyph of some effect ?
Effect effect_type;
int effect_timing; // time duration of current karaoke word
// after process_karaoke_effects: distance in pixels from the glyph origin.
// part of the glyph to the left of it is displayed in a different color.
int effect_skip_timing; // delay after the end of last karaoke word
int asc, desc; // font max ascender and descender
int be; // blur edges
double blur; // gaussian blur
double shadow_x;
double shadow_y;
double frx, fry, frz; // rotation
double fax, fay; // text shearing
double scale_x, scale_y;
double orig_scale_x, orig_scale_y; // scale_x,y before fix_glyph_scaling
int border_style;
double border_x, border_y;
double hspacing;
unsigned italic;
unsigned bold;
int flags;
int shape_run_id;
BitmapHashKey hash_key;
BitmapHashValue *image;
// next glyph in this cluster
struct glyph_info *next;
} GlyphInfo;
typedef struct {
double asc, desc;
int offset, len;
} LineInfo;
typedef struct {
GlyphInfo *glyphs;
int length;
LineInfo *lines;
int n_lines;
CombinedBitmapInfo *combined_bitmaps;
unsigned n_bitmaps;
double height;
int max_glyphs;
int max_lines;
unsigned max_bitmaps;
} TextInfo;
// Renderer state.
// Values like current font face, color, screen position, clipping and so on are stored here.
typedef struct {
ASS_Event *event;
ASS_Style *style;
int parsed_tags;
ASS_Font *font;
double font_size;
int flags; // decoration flags (underline/strike-through)
int alignment; // alignment overrides go here; if zero, style value will be used
int justify; // justify instructions
double frx, fry, frz;
double fax, fay; // text shearing
enum {
EVENT_NORMAL, // "normal" top-, sub- or mid- title
EVENT_POSITIONED, // happens after pos(,), margins are ignored
EVENT_HSCROLL, // "Banner" transition effect, text_width is unlimited
EVENT_VSCROLL // "Scroll up", "Scroll down" transition effects
} evt_type;
double pos_x, pos_y; // position
double org_x, org_y; // origin
char have_origin; // origin is explicitly defined; if 0, get_base_point() is used
double scale_x, scale_y;
double hspacing; // distance between letters, in pixels
int border_style;
double border_x; // outline width
double border_y;
uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA
int clip_x0, clip_y0, clip_x1, clip_y1;
char clip_mode; // 1 = iclip
char detect_collisions;
int fade; // alpha from \fad
char be; // blur edges
double blur; // gaussian blur
double shadow_x;
double shadow_y;
int drawing_scale; // currently reading: regular text if 0, drawing otherwise
double pbo; // drawing baseline offset
ASS_Drawing *clip_drawing; // clip vector
int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip
Effect effect_type;
int effect_timing;
int effect_skip_timing;
enum {
SCROLL_LR, // left-to-right
SCROLL_RL,
SCROLL_TB, // top-to-bottom
SCROLL_BT
} scroll_direction; // for EVENT_HSCROLL, EVENT_VSCROLL
int scroll_shift;
// face properties
char *family;
unsigned bold;
unsigned italic;
int treat_family_as_pattern;
int wrap_style;
int font_encoding;
// combination of ASS_OVERRIDE_BIT_* flags that apply right now
unsigned overrides;
// whether to apply font_scale
int apply_font_scale;
// whether this is assumed to be explicitly positioned
int explicit;
// used to store RenderContext.style when doing selective style overrides
ASS_Style override_style_temp_storage;
} RenderContext;
typedef struct {
Cache *font_cache;
Cache *outline_cache;
Cache *bitmap_cache;
Cache *composite_cache;
size_t glyph_max;
size_t bitmap_max_size;
size_t composite_max_size;
} CacheStore;
#include "ass_shaper.h"
struct ass_renderer {
ASS_Library *library;
FT_Library ftlibrary;
ASS_FontSelector *fontselect;
ASS_Settings settings;
int render_id;
ASS_Shaper *shaper;
ASS_Image *images_root; // rendering result is stored here
ASS_Image *prev_images_root;
EventImages *eimg; // temporary buffer for sorting rendered events
int eimg_size; // allocated buffer size
// frame-global data
int width, height; // screen dimensions
int orig_height; // frame height ( = screen height - margins )
int orig_width; // frame width ( = screen width - margins )
int orig_height_nocrop; // frame height ( = screen height - margins + cropheight)
int orig_width_nocrop; // frame width ( = screen width - margins + cropwidth)
ASS_Track *track;
long long time; // frame's timestamp, ms
double font_scale;
double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio
double border_scale;
double blur_scale;
RenderContext state;
TextInfo text_info;
CacheStore cache;
const BitmapEngine *engine;
RasterizerData rasterizer;
ASS_Style user_override_style;
};
typedef struct render_priv {
int top, height, left, width;
int render_id;
} RenderPriv;
typedef struct {
int x0;
int y0;
int x1;
int y1;
} Rect;
typedef struct {
int a, b; // top and height
int ha, hb; // left and width
} Segment;
void reset_render_context(ASS_Renderer *render_priv, ASS_Style *style);
void ass_frame_ref(ASS_Image *img);
void ass_frame_unref(ASS_Image *img);
// XXX: this is actually in ass.c, includes should be fixed later on
void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track);
#endif /* LIBASS_RENDER_H */

204
libass/ass_render_api.c Normal file
View File

@ -0,0 +1,204 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2010 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include "ass_render.h"
static void ass_reconfigure(ASS_Renderer *priv)
{
ASS_Settings *settings = &priv->settings;
priv->render_id++;
ass_cache_empty(priv->cache.composite_cache);
ass_cache_empty(priv->cache.bitmap_cache);
ass_cache_empty(priv->cache.outline_cache);
priv->width = settings->frame_width;
priv->height = settings->frame_height;
priv->orig_width = settings->frame_width - settings->left_margin -
settings->right_margin;
priv->orig_height = settings->frame_height - settings->top_margin -
settings->bottom_margin;
priv->orig_width_nocrop =
settings->frame_width - FFMAX(settings->left_margin, 0) -
FFMAX(settings->right_margin, 0);
priv->orig_height_nocrop =
settings->frame_height - FFMAX(settings->top_margin, 0) -
FFMAX(settings->bottom_margin, 0);
}
void ass_set_frame_size(ASS_Renderer *priv, int w, int h)
{
if (priv->settings.frame_width != w || priv->settings.frame_height != h) {
priv->settings.frame_width = w;
priv->settings.frame_height = h;
ass_reconfigure(priv);
}
}
void ass_set_storage_size(ASS_Renderer *priv, int w, int h)
{
if (priv->settings.storage_width != w ||
priv->settings.storage_height != h) {
priv->settings.storage_width = w;
priv->settings.storage_height = h;
ass_reconfigure(priv);
}
}
void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level)
{
#ifdef CONFIG_HARFBUZZ
// select the complex shaper for illegal values
if (level == ASS_SHAPING_SIMPLE || level == ASS_SHAPING_COMPLEX)
priv->settings.shaper = level;
else
priv->settings.shaper = ASS_SHAPING_COMPLEX;
#endif
}
void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r)
{
if (priv->settings.left_margin != l || priv->settings.right_margin != r ||
priv->settings.top_margin != t || priv->settings.bottom_margin != b) {
priv->settings.left_margin = l;
priv->settings.right_margin = r;
priv->settings.top_margin = t;
priv->settings.bottom_margin = b;
ass_reconfigure(priv);
}
}
void ass_set_use_margins(ASS_Renderer *priv, int use)
{
priv->settings.use_margins = use;
}
void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar)
{
ass_set_pixel_aspect(priv, dar / sar);
}
void ass_set_pixel_aspect(ASS_Renderer *priv, double par)
{
if (priv->settings.par != par) {
priv->settings.par = par;
ass_reconfigure(priv);
}
}
void ass_set_font_scale(ASS_Renderer *priv, double font_scale)
{
if (priv->settings.font_size_coeff != font_scale) {
priv->settings.font_size_coeff = font_scale;
ass_reconfigure(priv);
}
}
void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht)
{
if (priv->settings.hinting != ht) {
priv->settings.hinting = ht;
ass_reconfigure(priv);
}
}
void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing)
{
priv->settings.line_spacing = line_spacing;
}
void ass_set_line_position(ASS_Renderer *priv, double line_position)
{
if (priv->settings.line_position != line_position) {
priv->settings.line_position = line_position;
ass_reconfigure(priv);
}
}
void ass_set_fonts(ASS_Renderer *priv, const char *default_font,
const char *default_family, int dfp,
const char *config, int update)
{
free(priv->settings.default_font);
free(priv->settings.default_family);
priv->settings.default_font = default_font ? strdup(default_font) : 0;
priv->settings.default_family =
default_family ? strdup(default_family) : 0;
ass_reconfigure(priv);
ass_cache_empty(priv->cache.font_cache);
if (priv->shaper)
ass_shaper_empty_cache(priv->shaper);
if (priv->fontselect)
ass_fontselect_free(priv->fontselect);
priv->fontselect = ass_fontselect_init(priv->library, priv->ftlibrary,
default_family, default_font, config, dfp);
}
void ass_set_selective_style_override_enabled(ASS_Renderer *priv, int bits)
{
if (priv->settings.selective_style_overrides != bits) {
priv->settings.selective_style_overrides = bits;
ass_reconfigure(priv);
}
}
void ass_set_selective_style_override(ASS_Renderer *priv, ASS_Style *style)
{
ASS_Style *user_style = &priv->user_override_style;
free(user_style->FontName);
*user_style = *style;
user_style->FontName = strdup(user_style->FontName);
}
int ass_fonts_update(ASS_Renderer *render_priv)
{
// This is just a stub now!
return 1;
}
void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max,
int bitmap_max)
{
render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX;
size_t bitmap_cache, composite_cache;
if (bitmap_max) {
bitmap_cache = MEGABYTE * (size_t) bitmap_max;
composite_cache = bitmap_cache / (COMPOSITE_CACHE_RATIO + 1);
bitmap_cache -= composite_cache;
} else {
bitmap_cache = BITMAP_CACHE_MAX_SIZE;
composite_cache = COMPOSITE_CACHE_MAX_SIZE;
}
render_priv->cache.bitmap_max_size = bitmap_cache;
render_priv->cache.composite_max_size = composite_cache;
}
ASS_FontProvider *
ass_create_font_provider(ASS_Renderer *priv, ASS_FontProviderFuncs *funcs,
void *data)
{
return ass_font_provider_new(priv->fontselect, funcs, data);
}

996
libass/ass_shaper.c Normal file
View File

@ -0,0 +1,996 @@
/*
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include "ass_shaper.h"
#include "ass_render.h"
#include "ass_font.h"
#include "ass_parse.h"
#include "ass_cache.h"
#include <limits.h>
#include <stdbool.h>
#ifdef CONFIG_HARFBUZZ
#include <hb-ft.h>
enum {
VERT = 0,
VKNA,
KERN,
LIGA,
CLIG
};
#define NUM_FEATURES 5
#endif
struct ass_shaper {
ASS_ShapingLevel shaping_level;
// FriBidi log2vis
int n_glyphs;
FriBidiChar *event_text;
FriBidiCharType *ctypes;
FriBidiLevel *emblevels;
FriBidiStrIndex *cmap;
FriBidiParType base_direction;
#ifdef CONFIG_HARFBUZZ
// OpenType features
int n_features;
hb_feature_t *features;
hb_language_t language;
// Glyph metrics cache, to speed up shaping
Cache *metrics_cache;
#endif
};
#ifdef CONFIG_HARFBUZZ
struct ass_shaper_metrics_data {
Cache *metrics_cache;
GlyphMetricsHashKey hash_key;
int vertical;
};
struct ass_shaper_font_data {
hb_font_t *fonts[ASS_FONT_MAX_FACES];
hb_font_funcs_t *font_funcs[ASS_FONT_MAX_FACES];
struct ass_shaper_metrics_data *metrics_data[ASS_FONT_MAX_FACES];
};
#endif
/**
* \brief Print version information
*/
void ass_shaper_info(ASS_Library *lib)
{
ass_msg(lib, MSGL_INFO, "Shaper: FriBidi "
FRIBIDI_VERSION " (SIMPLE)"
#ifdef CONFIG_HARFBUZZ
" HarfBuzz-ng %s (COMPLEX)", hb_version_string()
#endif
);
}
/**
* \brief grow arrays, if needed
* \param new_size requested size
*/
static bool check_allocations(ASS_Shaper *shaper, size_t new_size)
{
if (new_size > shaper->n_glyphs) {
if (!ASS_REALLOC_ARRAY(shaper->event_text, new_size) ||
!ASS_REALLOC_ARRAY(shaper->ctypes, new_size) ||
!ASS_REALLOC_ARRAY(shaper->emblevels, new_size) ||
!ASS_REALLOC_ARRAY(shaper->cmap, new_size))
return false;
shaper->n_glyphs = new_size;
}
return true;
}
/**
* \brief Free shaper and related data
*/
void ass_shaper_free(ASS_Shaper *shaper)
{
#ifdef CONFIG_HARFBUZZ
ass_cache_done(shaper->metrics_cache);
free(shaper->features);
#endif
free(shaper->event_text);
free(shaper->ctypes);
free(shaper->emblevels);
free(shaper->cmap);
free(shaper);
}
void ass_shaper_empty_cache(ASS_Shaper *shaper)
{
#ifdef CONFIG_HARFBUZZ
ass_cache_empty(shaper->metrics_cache);
#endif
}
void ass_shaper_font_data_free(ASS_ShaperFontData *priv)
{
#ifdef CONFIG_HARFBUZZ
int i;
for (i = 0; i < ASS_FONT_MAX_FACES; i++)
if (priv->fonts[i]) {
free(priv->metrics_data[i]);
hb_font_destroy(priv->fonts[i]);
hb_font_funcs_destroy(priv->font_funcs[i]);
}
free(priv);
#endif
}
#ifdef CONFIG_HARFBUZZ
/**
* \brief set up the HarfBuzz OpenType feature list with some
* standard features.
*/
static bool init_features(ASS_Shaper *shaper)
{
shaper->features = calloc(sizeof(hb_feature_t), NUM_FEATURES);
if (!shaper->features)
return false;
shaper->n_features = NUM_FEATURES;
shaper->features[VERT].tag = HB_TAG('v', 'e', 'r', 't');
shaper->features[VERT].end = UINT_MAX;
shaper->features[VKNA].tag = HB_TAG('v', 'k', 'n', 'a');
shaper->features[VKNA].end = UINT_MAX;
shaper->features[KERN].tag = HB_TAG('k', 'e', 'r', 'n');
shaper->features[KERN].end = UINT_MAX;
shaper->features[LIGA].tag = HB_TAG('l', 'i', 'g', 'a');
shaper->features[LIGA].end = UINT_MAX;
shaper->features[CLIG].tag = HB_TAG('c', 'l', 'i', 'g');
shaper->features[CLIG].end = UINT_MAX;
return true;
}
/**
* \brief Set features depending on properties of the run
*/
static void set_run_features(ASS_Shaper *shaper, GlyphInfo *info)
{
// enable vertical substitutions for @font runs
if (info->font->desc.vertical)
shaper->features[VERT].value = shaper->features[VKNA].value = 1;
else
shaper->features[VERT].value = shaper->features[VKNA].value = 0;
// disable ligatures if horizontal spacing is non-standard
if (info->hspacing)
shaper->features[LIGA].value = shaper->features[CLIG].value = 0;
else
shaper->features[LIGA].value = shaper->features[CLIG].value = 1;
}
/**
* \brief Update HarfBuzz's idea of font metrics
* \param hb_font HarfBuzz font
* \param face associated FreeType font face
*/
static void update_hb_size(hb_font_t *hb_font, FT_Face face)
{
hb_font_set_scale (hb_font,
((uint64_t) face->size->metrics.x_scale * (uint64_t) face->units_per_EM) >> 16,
((uint64_t) face->size->metrics.y_scale * (uint64_t) face->units_per_EM) >> 16);
hb_font_set_ppem (hb_font, face->size->metrics.x_ppem,
face->size->metrics.y_ppem);
}
/*
* Cached glyph metrics getters follow
*
* These functions replace HarfBuzz' standard FreeType font functions
* and provide cached access to essential glyph metrics. This usually
* speeds up shaping a lot. It also allows us to use custom load flags.
*
*/
GlyphMetricsHashValue *
get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face,
hb_codepoint_t unicode, hb_codepoint_t glyph)
{
GlyphMetricsHashValue *val;
metrics->hash_key.glyph_index = glyph;
if (ass_cache_get(metrics->metrics_cache, &metrics->hash_key, &val)) {
if (val->metrics.width >= 0)
return val;
ass_cache_dec_ref(val);
return NULL;
}
if (!val)
return NULL;
int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
| FT_LOAD_IGNORE_TRANSFORM;
if (FT_Load_Glyph(face, glyph, load_flags)) {
val->metrics.width = -1;
ass_cache_commit(val, 1);
ass_cache_dec_ref(val);
return NULL;
}
memcpy(&val->metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics));
// if @font rendering is enabled and the glyph should be rotated,
// make cached_h_advance pick up the right advance later
if (metrics->vertical && unicode >= VERTICAL_LOWER_BOUND)
val->metrics.horiAdvance = val->metrics.vertAdvance;
ass_cache_commit(val, 1);
return val;
}
static hb_bool_t
get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data)
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
if (variation)
*glyph = FT_Face_GetCharVariantIndex(face, ass_font_index_magic(face, unicode), variation);
else
*glyph = FT_Get_Char_Index(face, ass_font_index_magic(face, unicode));
if (!*glyph)
return false;
// rotate glyph advances for @fonts while we still know the Unicode codepoints
GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, unicode, *glyph);
ass_cache_dec_ref(metrics);
return true;
}
static hb_position_t
cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
void *user_data)
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metrics)
return 0;
hb_position_t advance = metrics->metrics.horiAdvance;
ass_cache_dec_ref(metrics);
return advance;
}
static hb_position_t
cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
void *user_data)
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metrics)
return 0;
hb_position_t advance = metrics->metrics.vertAdvance;
ass_cache_dec_ref(metrics);
return advance;
}
static hb_bool_t
cached_h_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y, void *user_data)
{
return true;
}
static hb_bool_t
cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y, void *user_data)
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metrics)
return false;
*x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX;
*y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY);
ass_cache_dec_ref(metrics);
return true;
}
static hb_position_t
get_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first,
hb_codepoint_t second, void *user_data)
{
FT_Face face = font_data;
FT_Vector kern;
if (FT_Get_Kerning(face, first, second, FT_KERNING_DEFAULT, &kern))
return 0;
return kern.x;
}
static hb_position_t
get_v_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first,
hb_codepoint_t second, void *user_data)
{
return 0;
}
static hb_bool_t
cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
hb_glyph_extents_t *extents, void *user_data)
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metrics)
return false;
extents->x_bearing = metrics->metrics.horiBearingX;
extents->y_bearing = metrics->metrics.horiBearingY;
extents->width = metrics->metrics.width;
extents->height = -metrics->metrics.height;
ass_cache_dec_ref(metrics);
return true;
}
static hb_bool_t
get_contour_point(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
unsigned int point_index, hb_position_t *x,
hb_position_t *y, void *user_data)
{
FT_Face face = font_data;
int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
| FT_LOAD_IGNORE_TRANSFORM;
if (FT_Load_Glyph(face, glyph, load_flags))
return false;
if (point_index >= (unsigned)face->glyph->outline.n_points)
return false;
*x = face->glyph->outline.points[point_index].x;
*y = face->glyph->outline.points[point_index].y;
return true;
}
/**
* \brief Retrieve HarfBuzz font from cache.
* Create it from FreeType font, if needed.
* \param info glyph cluster
* \return HarfBuzz font
*/
static hb_font_t *get_hb_font(ASS_Shaper *shaper, GlyphInfo *info)
{
ASS_Font *font = info->font;
hb_font_t **hb_fonts;
if (!font->shaper_priv)
font->shaper_priv = calloc(sizeof(ASS_ShaperFontData), 1);
hb_fonts = font->shaper_priv->fonts;
if (!hb_fonts[info->face_index]) {
hb_fonts[info->face_index] =
hb_ft_font_create(font->faces[info->face_index], NULL);
// set up cached metrics access
font->shaper_priv->metrics_data[info->face_index] =
calloc(sizeof(struct ass_shaper_metrics_data), 1);
struct ass_shaper_metrics_data *metrics =
font->shaper_priv->metrics_data[info->face_index];
metrics->metrics_cache = shaper->metrics_cache;
metrics->vertical = info->font->desc.vertical;
hb_font_funcs_t *funcs = hb_font_funcs_create();
font->shaper_priv->font_funcs[info->face_index] = funcs;
hb_font_funcs_set_glyph_func(funcs, get_glyph,
metrics, NULL);
hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance,
metrics, NULL);
hb_font_funcs_set_glyph_v_advance_func(funcs, cached_v_advance,
metrics, NULL);
hb_font_funcs_set_glyph_h_origin_func(funcs, cached_h_origin,
metrics, NULL);
hb_font_funcs_set_glyph_v_origin_func(funcs, cached_v_origin,
metrics, NULL);
hb_font_funcs_set_glyph_h_kerning_func(funcs, get_h_kerning,
metrics, NULL);
hb_font_funcs_set_glyph_v_kerning_func(funcs, get_v_kerning,
metrics, NULL);
hb_font_funcs_set_glyph_extents_func(funcs, cached_extents,
metrics, NULL);
hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point,
metrics, NULL);
hb_font_set_funcs(hb_fonts[info->face_index], funcs,
font->faces[info->face_index], NULL);
}
ass_face_set_size(font->faces[info->face_index], info->font_size);
update_hb_size(hb_fonts[info->face_index], font->faces[info->face_index]);
// update hash key for cached metrics
struct ass_shaper_metrics_data *metrics =
font->shaper_priv->metrics_data[info->face_index];
metrics->hash_key.font = info->font;
metrics->hash_key.face_index = info->face_index;
metrics->hash_key.size = info->font_size;
metrics->hash_key.scale_x = double_to_d6(info->scale_x);
metrics->hash_key.scale_y = double_to_d6(info->scale_y);
return hb_fonts[info->face_index];
}
/**
* \brief Map script to default language.
*
* This maps a script to a language, if a script has a representative
* language it is typically used with. Otherwise, the invalid language
* is returned.
*
* The mapping is similar to Pango's pango-language.c.
*
* \param script script tag
* \return language tag
*/
static hb_language_t script_to_language(hb_script_t script)
{
switch (script) {
// Unicode 1.1
case HB_SCRIPT_ARABIC: return hb_language_from_string("ar", -1); break;
case HB_SCRIPT_ARMENIAN: return hb_language_from_string("hy", -1); break;
case HB_SCRIPT_BENGALI: return hb_language_from_string("bn", -1); break;
case HB_SCRIPT_CANADIAN_ABORIGINAL: return hb_language_from_string("iu", -1); break;
case HB_SCRIPT_CHEROKEE: return hb_language_from_string("chr", -1); break;
case HB_SCRIPT_COPTIC: return hb_language_from_string("cop", -1); break;
case HB_SCRIPT_CYRILLIC: return hb_language_from_string("ru", -1); break;
case HB_SCRIPT_DEVANAGARI: return hb_language_from_string("hi", -1); break;
case HB_SCRIPT_GEORGIAN: return hb_language_from_string("ka", -1); break;
case HB_SCRIPT_GREEK: return hb_language_from_string("el", -1); break;
case HB_SCRIPT_GUJARATI: return hb_language_from_string("gu", -1); break;
case HB_SCRIPT_GURMUKHI: return hb_language_from_string("pa", -1); break;
case HB_SCRIPT_HANGUL: return hb_language_from_string("ko", -1); break;
case HB_SCRIPT_HEBREW: return hb_language_from_string("he", -1); break;
case HB_SCRIPT_HIRAGANA: return hb_language_from_string("ja", -1); break;
case HB_SCRIPT_KANNADA: return hb_language_from_string("kn", -1); break;
case HB_SCRIPT_KATAKANA: return hb_language_from_string("ja", -1); break;
case HB_SCRIPT_LAO: return hb_language_from_string("lo", -1); break;
case HB_SCRIPT_LATIN: return hb_language_from_string("en", -1); break;
case HB_SCRIPT_MALAYALAM: return hb_language_from_string("ml", -1); break;
case HB_SCRIPT_MONGOLIAN: return hb_language_from_string("mn", -1); break;
case HB_SCRIPT_ORIYA: return hb_language_from_string("or", -1); break;
case HB_SCRIPT_SYRIAC: return hb_language_from_string("syr", -1); break;
case HB_SCRIPT_TAMIL: return hb_language_from_string("ta", -1); break;
case HB_SCRIPT_TELUGU: return hb_language_from_string("te", -1); break;
case HB_SCRIPT_THAI: return hb_language_from_string("th", -1); break;
// Unicode 2.0
case HB_SCRIPT_TIBETAN: return hb_language_from_string("bo", -1); break;
// Unicode 3.0
case HB_SCRIPT_ETHIOPIC: return hb_language_from_string("am", -1); break;
case HB_SCRIPT_KHMER: return hb_language_from_string("km", -1); break;
case HB_SCRIPT_MYANMAR: return hb_language_from_string("my", -1); break;
case HB_SCRIPT_SINHALA: return hb_language_from_string("si", -1); break;
case HB_SCRIPT_THAANA: return hb_language_from_string("dv", -1); break;
// Unicode 3.2
case HB_SCRIPT_BUHID: return hb_language_from_string("bku", -1); break;
case HB_SCRIPT_HANUNOO: return hb_language_from_string("hnn", -1); break;
case HB_SCRIPT_TAGALOG: return hb_language_from_string("tl", -1); break;
case HB_SCRIPT_TAGBANWA: return hb_language_from_string("tbw", -1); break;
// Unicode 4.0
case HB_SCRIPT_UGARITIC: return hb_language_from_string("uga", -1); break;
// Unicode 4.1
case HB_SCRIPT_BUGINESE: return hb_language_from_string("bug", -1); break;
case HB_SCRIPT_OLD_PERSIAN: return hb_language_from_string("peo", -1); break;
case HB_SCRIPT_SYLOTI_NAGRI: return hb_language_from_string("syl", -1); break;
// Unicode 5.0
case HB_SCRIPT_NKO: return hb_language_from_string("nko", -1); break;
// no representative language exists
default: return HB_LANGUAGE_INVALID; break;
}
}
/**
* \brief Determine language to be used for shaping a run.
*
* \param shaper shaper instance
* \param script script tag associated with run
* \return language tag
*/
static hb_language_t
hb_shaper_get_run_language(ASS_Shaper *shaper, hb_script_t script)
{
hb_language_t lang;
// override set, use it
if (shaper->language != HB_LANGUAGE_INVALID)
return shaper->language;
// get default language for given script
lang = script_to_language(script);
// no dice, use system default
if (lang == HB_LANGUAGE_INVALID)
lang = hb_language_get_default();
return lang;
}
/**
* \brief Feed a run of shaped characters into the GlyphInfo array.
*
* \param glyphs GlyphInfo array
* \param buf buffer of shaped run
* \param offset offset into GlyphInfo array
*/
static void
shape_harfbuzz_process_run(GlyphInfo *glyphs, hb_buffer_t *buf, int offset)
{
int j;
int num_glyphs = hb_buffer_get_length(buf);
hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, NULL);
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buf, NULL);
for (j = 0; j < num_glyphs; j++) {
unsigned idx = glyph_info[j].cluster + offset;
GlyphInfo *info = glyphs + idx;
GlyphInfo *root = info;
// if we have more than one glyph per cluster, allocate a new one
// and attach to the root glyph
if (info->skip == 0) {
while (info->next)
info = info->next;
info->next = malloc(sizeof(GlyphInfo));
if (info->next) {
memcpy(info->next, info, sizeof(GlyphInfo));
ass_cache_inc_ref(info->font);
info = info->next;
info->next = NULL;
}
}
// set position and advance
info->skip = 0;
info->glyph_index = glyph_info[j].codepoint;
info->offset.x = pos[j].x_offset * info->scale_x;
info->offset.y = -pos[j].y_offset * info->scale_y;
info->advance.x = pos[j].x_advance * info->scale_x;
info->advance.y = -pos[j].y_advance * info->scale_y;
// accumulate advance in the root glyph
root->cluster_advance.x += info->advance.x;
root->cluster_advance.y += info->advance.y;
}
}
/**
* \brief Shape event text with HarfBuzz. Full OpenType shaping.
* \param glyphs glyph clusters
* \param len number of clusters
*/
static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
{
int i;
hb_buffer_t *buf = hb_buffer_create();
hb_segment_properties_t props = HB_SEGMENT_PROPERTIES_DEFAULT;
// Initialize: skip all glyphs, this is undone later as needed
for (i = 0; i < len; i++)
glyphs[i].skip = 1;
for (i = 0; i < len; i++) {
int offset = i;
hb_font_t *font = get_hb_font(shaper, glyphs + offset);
int level = glyphs[offset].shape_run_id;
int direction = shaper->emblevels[offset] % 2;
// advance in text until end of run
while (i < (len - 1) && level == glyphs[i+1].shape_run_id)
i++;
hb_buffer_pre_allocate(buf, i - offset + 1);
hb_buffer_add_utf32(buf, shaper->event_text + offset, i - offset + 1,
0, i - offset + 1);
props.direction = direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
props.script = glyphs[offset].script;
props.language = hb_shaper_get_run_language(shaper, props.script);
hb_buffer_set_segment_properties(buf, &props);
set_run_features(shaper, glyphs + offset);
hb_shape(font, buf, shaper->features, shaper->n_features);
shape_harfbuzz_process_run(glyphs, buf, offset);
hb_buffer_reset(buf);
}
hb_buffer_destroy(buf);
}
/**
* \brief Determine script property of all characters. Characters of script
* common and inherited get their script from their context.
*
*/
void ass_shaper_determine_script(ASS_Shaper *shaper, GlyphInfo *glyphs,
size_t len)
{
int i;
int backwards_scan = 0;
hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
hb_script_t last_script = HB_SCRIPT_UNKNOWN;
// determine script (forward scan)
for (i = 0; i < len; i++) {
GlyphInfo *info = glyphs + i;
info->script = hb_unicode_script(ufuncs, info->symbol);
// common/inherit codepoints inherit script from context
if (info->script == HB_SCRIPT_COMMON ||
info->script == HB_SCRIPT_INHERITED) {
// unknown is not a valid context
if (last_script != HB_SCRIPT_UNKNOWN)
info->script = last_script;
else
// do a backwards scan to check if next codepoint
// contains a valid script for context
backwards_scan = 1;
} else {
last_script = info->script;
}
}
// determine script (backwards scan, if needed)
last_script = HB_SCRIPT_UNKNOWN;
for (i = len - 1; i >= 0 && backwards_scan; i--) {
GlyphInfo *info = glyphs + i;
// common/inherit codepoints inherit script from context
if (info->script == HB_SCRIPT_COMMON ||
info->script == HB_SCRIPT_INHERITED) {
// unknown script is not a valid context
if (last_script != HB_SCRIPT_UNKNOWN)
info->script = last_script;
} else {
last_script = info->script;
}
}
}
#endif
/**
* \brief Shape event text with FriBidi. Does mirroring and simple
* Arabic shaping.
* \param len number of clusters
*/
static void shape_fribidi(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
{
int i;
FriBidiJoiningType *joins = calloc(sizeof(*joins), len);
// shape on codepoint level
fribidi_get_joining_types(shaper->event_text, len, joins);
fribidi_join_arabic(shaper->ctypes, len, shaper->emblevels, joins);
fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC,
shaper->emblevels, len, joins, shaper->event_text);
// update indexes
for (i = 0; i < len; i++) {
GlyphInfo *info = glyphs + i;
FT_Face face = info->font->faces[info->face_index];
info->symbol = shaper->event_text[i];
info->glyph_index = FT_Get_Char_Index(face, ass_font_index_magic(face, shaper->event_text[i]));
}
free(joins);
}
/**
* \brief Toggle kerning for HarfBuzz shaping.
* \param shaper shaper instance
* \param kern toggle kerning
*/
void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern)
{
#ifdef CONFIG_HARFBUZZ
shaper->features[KERN].value = !!kern;
#endif
}
/**
* \brief Find shape runs according to the event's selected fonts
*/
void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
GlyphInfo *glyphs, size_t len)
{
int i;
int shape_run = 0;
#ifdef CONFIG_HARFBUZZ
ass_shaper_determine_script(shaper, glyphs, len);
#endif
// find appropriate fonts for the shape runs
for (i = 0; i < len; i++) {
GlyphInfo *last = glyphs + i - 1;
GlyphInfo *info = glyphs + i;
// skip drawings
if (info->symbol == 0xfffc)
continue;
// set size and get glyph index
ass_font_get_index(render_priv->fontselect, info->font,
info->symbol, &info->face_index, &info->glyph_index);
// shape runs break on: xbord, ybord, xshad, yshad,
// all four colors, all four alphas, be, blur, fn, fs,
// fscx, fscy, fsp, bold, italic, underline, strikeout,
// frx, fry, frz, fax, fay, karaoke start, karaoke type,
// and on every line break
if (i > 0 && (last->font != info->font ||
last->face_index != info->face_index ||
last->script != info->script ||
last->font_size != info->font_size ||
last->c[0] != info->c[0] ||
last->c[1] != info->c[1] ||
last->c[2] != info->c[2] ||
last->c[3] != info->c[3] ||
last->be != info->be ||
last->blur != info->blur ||
last->shadow_x != info->shadow_x ||
last->shadow_y != info->shadow_y ||
last->frx != info->frx ||
last->fry != info->fry ||
last->frz != info->frz ||
last->fax != info->fax ||
last->fay != info->fay ||
last->scale_x != info->scale_x ||
last->scale_y != info->scale_y ||
last->border_style != info->border_style ||
last->border_x != info->border_x ||
last->border_y != info->border_y ||
last->hspacing != info->hspacing ||
last->italic != info->italic ||
last->bold != info->bold ||
last->flags != info->flags))
shape_run++;
info->shape_run_id = shape_run;
}
}
/**
* \brief Set base direction (paragraph direction) of the text.
* \param dir base direction
*/
void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir)
{
shaper->base_direction = dir;
}
/**
* \brief Set language hint. Some languages have specific character variants,
* like Serbian Cyrillic.
* \param lang ISO 639-1 two-letter language code
*/
void ass_shaper_set_language(ASS_Shaper *shaper, const char *code)
{
#ifdef CONFIG_HARFBUZZ
hb_language_t lang;
if (code)
lang = hb_language_from_string(code, -1);
else
lang = HB_LANGUAGE_INVALID;
shaper->language = lang;
#endif
}
/**
* Set shaping level. Essentially switches between FriBidi and HarfBuzz.
*/
void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level)
{
shaper->shaping_level = level;
}
/**
* \brief Remove all zero-width invisible characters from the text.
* \param text_info text
*/
static void ass_shaper_skip_characters(TextInfo *text_info)
{
int i;
GlyphInfo *glyphs = text_info->glyphs;
for (i = 0; i < text_info->length; i++) {
// Skip direction override control characters
if ((glyphs[i].symbol <= 0x202e && glyphs[i].symbol >= 0x202a)
|| (glyphs[i].symbol <= 0x200f && glyphs[i].symbol >= 0x200b)
|| (glyphs[i].symbol <= 0x2063 && glyphs[i].symbol >= 0x2060)
|| glyphs[i].symbol == 0xfeff
|| glyphs[i].symbol == 0x00ad
|| glyphs[i].symbol == 0x034f) {
glyphs[i].symbol = 0;
glyphs[i].skip++;
}
}
}
/**
* \brief Shape an event's text. Calculates directional runs and shapes them.
* \param text_info event's text
* \return success, when 0
*/
int ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info)
{
int i, ret, last_break;
FriBidiParType dir;
GlyphInfo *glyphs = text_info->glyphs;
if (!check_allocations(shaper, text_info->length))
return -1;
// Get bidi character types and embedding levels
last_break = 0;
for (i = 0; i < text_info->length; i++) {
shaper->event_text[i] = glyphs[i].symbol;
// embedding levels should be calculated paragraph by paragraph
if (glyphs[i].symbol == '\n' || i == text_info->length - 1) {
dir = shaper->base_direction;
fribidi_get_bidi_types(shaper->event_text + last_break,
i - last_break + 1, shaper->ctypes + last_break);
ret = fribidi_get_par_embedding_levels(shaper->ctypes + last_break,
i - last_break + 1, &dir, shaper->emblevels + last_break);
if (ret == 0)
return -1;
last_break = i + 1;
}
}
// add embedding levels to shape runs for final runs
for (i = 0; i < text_info->length; i++) {
glyphs[i].shape_run_id += shaper->emblevels[i];
}
#ifdef CONFIG_HARFBUZZ
switch (shaper->shaping_level) {
case ASS_SHAPING_SIMPLE:
shape_fribidi(shaper, glyphs, text_info->length);
ass_shaper_skip_characters(text_info);
break;
case ASS_SHAPING_COMPLEX:
shape_harfbuzz(shaper, glyphs, text_info->length);
break;
}
#else
shape_fribidi(shaper, glyphs, text_info->length);
ass_shaper_skip_characters(text_info);
#endif
return 0;
}
/**
* \brief Create a new shaper instance and preallocate data structures
* \param prealloc preallocation size
*/
ASS_Shaper *ass_shaper_new(size_t prealloc)
{
ASS_Shaper *shaper = calloc(sizeof(*shaper), 1);
if (!shaper)
return NULL;
shaper->base_direction = FRIBIDI_PAR_ON;
if (!check_allocations(shaper, prealloc))
goto error;
#ifdef CONFIG_HARFBUZZ
if (!init_features(shaper))
goto error;
shaper->metrics_cache = ass_glyph_metrics_cache_create();
if (!shaper->metrics_cache)
goto error;
#endif
return shaper;
error:
ass_shaper_free(shaper);
return NULL;
}
/**
* \brief clean up additional data temporarily needed for shaping and
* (e.g. additional glyphs allocated)
*/
void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info)
{
int i;
for (i = 0; i < text_info->length; i++) {
GlyphInfo *info = text_info->glyphs + i;
info = info->next;
while (info) {
GlyphInfo *next = info->next;
free(info);
info = next;
}
}
}
/**
* \brief Calculate reorder map to render glyphs in visual order
* \param shaper shaper instance
* \param text_info text to be reordered
* \return map of reordered characters, or NULL
*/
FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info)
{
int i, ret;
// Initialize reorder map
for (i = 0; i < text_info->length; i++)
shaper->cmap[i] = i;
// Create reorder map line-by-line
for (i = 0; i < text_info->n_lines; i++) {
LineInfo *line = text_info->lines + i;
FriBidiParType dir = FRIBIDI_PAR_ON;
ret = fribidi_reorder_line(0,
shaper->ctypes + line->offset, line->len, 0, dir,
shaper->emblevels + line->offset, NULL,
shaper->cmap + line->offset);
if (ret == 0)
return NULL;
}
return shaper->cmap;
}
/**
* \brief Resolve a Windows font charset number to a suitable base
* direction. Generally, use LTR for compatibility with VSFilter. The
* special value -1, which is not a legal Windows font charset number,
* can be used for autodetection.
* \param enc Windows font encoding
*/
FriBidiParType resolve_base_direction(int enc)
{
switch (enc) {
case -1:
return FRIBIDI_PAR_ON;
default:
return FRIBIDI_PAR_LTR;
}
}

44
libass/ass_shaper.h Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_SHAPER_H
#define LIBASS_SHAPER_H
typedef struct ass_shaper ASS_Shaper;
#include <fribidi.h>
#include "ass_render.h"
void ass_shaper_info(ASS_Library *lib);
ASS_Shaper *ass_shaper_new(size_t prealloc);
void ass_shaper_free(ASS_Shaper *shaper);
void ass_shaper_empty_cache(ASS_Shaper *shaper);
void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern);
void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
GlyphInfo *glyphs, size_t len);
void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir);
void ass_shaper_set_language(ASS_Shaper *shaper, const char *code);
void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level);
int ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info);
void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info);
FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info);
FriBidiParType resolve_base_direction(int font_encoding);
void ass_shaper_font_data_free(ASS_ShaperFontData *priv);
#endif

75
libass/ass_string.c Normal file
View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2015 Grigori Goronzy <greg@kinoho.net>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include "ass_string.h"
static const unsigned char lowertab[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61,
0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
0xfd, 0xfe, 0xff
};
int ass_strcasecmp(const char *s1, const char *s2)
{
unsigned char a, b;
do {
a = lowertab[(unsigned char)*s1++];
b = lowertab[(unsigned char)*s2++];
} while (a && a == b);
return a - b;
}
int ass_strncasecmp(const char *s1, const char *s2, size_t n)
{
unsigned char a, b;
const char *last = s1 + n;
do {
a = lowertab[(unsigned char)*s1++];
b = lowertab[(unsigned char)*s2++];
} while (s1 < last && a && a == b);
return a - b;
}

38
libass/ass_string.h Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2015 Grigori Goronzy <greg@kinoho.net>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#ifndef ASS_STRING_H
#define ASS_STRING_H
int ass_strcasecmp(const char *s1, const char *s2);
int ass_strncasecmp(const char *s1, const char *s2, size_t n);
static inline int ass_isspace(int c)
{
return c == ' ' || c == '\t' || c == '\n' || c == '\v' ||
c == '\f' || c == '\r';
}
static inline int ass_isdigit(int c)
{
return c >= '0' && c <= '9';
}
#endif

337
libass/ass_strtod.c Normal file
View File

@ -0,0 +1,337 @@
/*
* Copyright (c) 1988-1993 The Regents of the University of California.
* Copyright (c) 1994 Sun Microsystems, Inc.
* Copyright (c) 2016 Oleg Oshmyan <chortos@inbox.lv>
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*
*/
#include "config.h"
#include "ass_compat.h"
#include <stdlib.h>
#include <float.h>
#include <errno.h>
#include "ass_string.h"
static
const size_t maxExponent = 511; /* Largest possible base 10 exponent. Any
* exponent larger than this will already
* produce underflow or overflow, so there's
* no need to worry about additional digits.
*/
static
const double powersOf10[] = { /* Table giving binary powers of 10. Entry */
10., /* is 10^2^i. Used to convert decimal */
100., /* exponents into floating-point numbers. */
1.0e4,
1.0e8,
1.0e16,
1.0e32,
1.0e64,
1.0e128,
1.0e256
};
static
const double negPowOf10[] = { /* Table giving negative binary powers */
0.1, /* of 10. Entry is 10^-2^i. */
0.01, /* Used to convert decimal exponents */
1.0e-4, /* into floating-point numbers. */
1.0e-8,
1.0e-16,
1.0e-32,
1.0e-64,
1.0e-128,
1.0e-256
};
/*
*----------------------------------------------------------------------
*
* strtod --
*
* This procedure converts a floating-point number from an ASCII
* decimal representation to internal double-precision format.
*
* Results:
* The return value is the double-precision floating-point
* representation of the characters in string. If endPtr isn't
* NULL, then *endPtr is filled in with the address of the
* next character after the last one that was part of the
* floating-point number.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
double
ass_strtod(
const char *string, /* A decimal ASCII floating-point number,
* optionally preceded by white space.
* Must have form "-I.FE-X", where I is the
* integer part of the mantissa, F is the
* fractional part of the mantissa, and X
* is the exponent. Either of the signs
* may be "+", "-", or omitted. Either I
* or F may be omitted, or both. The decimal
* point isn't necessary unless F is present.
* The "E" may actually be an "e". E and X
* may both be omitted (but not just one).
*/
char **endPtr /* If non-NULL, store terminating character's
* address here. */
)
{
int sign, fracExpSign, expSign;
double fraction, dblExp;
const double *d;
register const char *p;
register int c;
size_t exp = 0; /* Exponent read from "EX" field. */
size_t fracExp; /* Exponent that derives from the fractional
* part. Under normal circumstatnces, it is
* the negative of the number of digits in F.
* However, if I is very long, the last digits
* of I get dropped (otherwise a long I with a
* large negative exponent could cause an
* unnecessary overflow on I alone). In this
* case, fracExp is incremented one for each
* dropped digit. */
size_t mantSize; /* Number of digits in mantissa. */
size_t decPt; /* Number of mantissa digits BEFORE decimal
* point. */
size_t leadZeros; /* Number of leading zeros in mantissa. */
const char *pExp; /* Temporarily holds location of exponent
* in string. */
/*
* Strip off leading blanks and check for a sign.
*/
p = string;
while (ass_isspace(*p)) {
p += 1;
}
if (*p == '-') {
sign = 1;
p += 1;
} else {
if (*p == '+') {
p += 1;
}
sign = 0;
}
/*
* Count the number of digits in the mantissa (including the decimal
* point), and also locate the decimal point.
*/
decPt = -1;
leadZeros = -1;
for (mantSize = 0; ; mantSize += 1)
{
c = *p;
if (!ass_isdigit(c)) {
if ((c != '.') || (decPt != (size_t) -1)) {
break;
}
decPt = mantSize;
} else if ((c != '0') && (leadZeros == (size_t) -1)) {
leadZeros = mantSize;
}
p += 1;
}
/*
* Now suck up the digits in the mantissa. Use two integers to
* collect 9 digits each (this is faster than using floating-point).
* If the mantissa has more than 18 digits, ignore the extras, since
* they can't affect the value anyway.
*/
if (leadZeros == (size_t) -1) {
leadZeros = mantSize;
}
pExp = p;
p -= mantSize - leadZeros;
if (decPt == (size_t) -1) {
decPt = mantSize;
} else {
mantSize -= 1; /* One of the digits was the point. */
if (decPt < leadZeros) {
leadZeros -= 1;
}
}
if (mantSize - leadZeros > 18) {
mantSize = leadZeros + 18;
}
if (decPt < mantSize) {
fracExpSign = 1;
fracExp = mantSize - decPt;
} else {
fracExpSign = 0;
fracExp = decPt - mantSize;
}
if (mantSize == 0) {
fraction = 0.0;
p = string;
goto done;
} else {
int frac1, frac2, m;
mantSize -= leadZeros;
m = mantSize;
frac1 = 0;
for ( ; m > 9; m -= 1)
{
c = *p;
p += 1;
if (c == '.') {
c = *p;
p += 1;
}
frac1 = 10*frac1 + (c - '0');
}
frac2 = 0;
for (; m > 0; m -= 1)
{
c = *p;
p += 1;
if (c == '.') {
c = *p;
p += 1;
}
frac2 = 10*frac2 + (c - '0');
}
fraction = (1.0e9 * frac1) + frac2;
}
/*
* Skim off the exponent.
*/
p = pExp;
if ((*p == 'E') || (*p == 'e')) {
size_t expLimit; /* If exp > expLimit, appending another digit
* to exp is guaranteed to make it too large.
* If exp == expLimit, this may depend on
* the exact digit, but in any case exp with
* the digit appended and fracExp added will
* still fit in size_t, even if it does
* exceed maxExponent. */
int expWraparound = 0;
p += 1;
if (*p == '-') {
expSign = 1;
p += 1;
} else {
if (*p == '+') {
p += 1;
}
expSign = 0;
}
if (expSign == fracExpSign) {
if (maxExponent < fracExp) {
expLimit = 0;
} else {
expLimit = (maxExponent - fracExp) / 10;
}
} else {
expLimit = fracExp / 10 + (fracExp % 10 + maxExponent) / 10;
}
while (ass_isdigit(*p)) {
if ((exp > expLimit) || expWraparound) {
do {
p += 1;
} while (ass_isdigit(*p));
goto expOverflow;
} else if (exp > ((size_t) -1 - (*p - '0')) / 10) {
expWraparound = 1;
}
exp = exp * 10 + (*p - '0');
p += 1;
}
if (expSign == fracExpSign) {
exp = fracExp + exp;
} else if ((fracExp <= exp) || expWraparound) {
exp = exp - fracExp;
} else {
exp = fracExp - exp;
expSign = fracExpSign;
}
} else {
exp = fracExp;
expSign = fracExpSign;
}
/*
* Generate a floating-point number that represents the exponent.
* Do this by processing the exponent one bit at a time to combine
* many powers of 2 of 10. Then combine the exponent with the
* fraction.
*/
if (exp > maxExponent) {
expOverflow:
exp = maxExponent;
if (fraction != 0.0) {
errno = ERANGE;
}
}
/* Prefer positive powers of 10 for increased precision, especially
* for small powers that are represented exactly in floating-point. */
if ((exp <= DBL_MAX_10_EXP) || !expSign) {
d = powersOf10;
} else {
/* The floating-point format supports more negative exponents
* than positive, or perhaps the result is a subnormal number. */
if (exp > -DBL_MIN_10_EXP) {
/* The result might be a valid subnormal number, but the
* exponent underflows. Tweak fraction so that it is below
* 1.0 first, so that if the exponent still underflows after
* that, the result is sure to underflow as well. */
exp -= mantSize;
dblExp = 1.0;
for (d = powersOf10; mantSize != 0; mantSize >>= 1, d += 1) {
if (mantSize & 01) {
dblExp *= *d;
}
}
fraction /= dblExp;
}
d = negPowOf10;
expSign = 0;
}
dblExp = 1.0;
for (; exp != 0; exp >>= 1, d += 1) {
if (exp & 01) {
dblExp *= *d;
}
}
if (expSign) {
fraction /= dblExp;
} else {
fraction *= dblExp;
}
done:
if (endPtr != NULL) {
*endPtr = (char *) p;
}
if (sign) {
return -fraction;
}
return fraction;
}

210
libass/ass_types.h Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_TYPES_H
#define LIBASS_TYPES_H
#include <stdint.h>
#define VALIGN_SUB 0
#define VALIGN_CENTER 8
#define VALIGN_TOP 4
#define HALIGN_LEFT 1
#define HALIGN_CENTER 2
#define HALIGN_RIGHT 3
#define ASS_JUSTIFY_AUTO 0
#define ASS_JUSTIFY_LEFT 1
#define ASS_JUSTIFY_CENTER 2
#define ASS_JUSTIFY_RIGHT 3
#define FONT_WEIGHT_LIGHT 300
#define FONT_WEIGHT_MEDIUM 400
#define FONT_WEIGHT_BOLD 700
#define FONT_SLANT_NONE 0
#define FONT_SLANT_ITALIC 100
#define FONT_SLANT_OBLIQUE 110
#define FONT_WIDTH_CONDENSED 75
#define FONT_WIDTH_NORMAL 100
#define FONT_WIDTH_EXPANDED 125
/* Opaque objects internally used by libass. Contents are private. */
typedef struct ass_renderer ASS_Renderer;
typedef struct render_priv ASS_RenderPriv;
typedef struct parser_priv ASS_ParserPriv;
typedef struct ass_library ASS_Library;
/* ASS Style: line */
typedef struct ass_style {
char *Name;
char *FontName;
double FontSize;
uint32_t PrimaryColour;
uint32_t SecondaryColour;
uint32_t OutlineColour;
uint32_t BackColour;
int Bold;
int Italic;
int Underline;
int StrikeOut;
double ScaleX;
double ScaleY;
double Spacing;
double Angle;
int BorderStyle;
double Outline;
double Shadow;
int Alignment;
int MarginL;
int MarginR;
int MarginV;
int Encoding;
int treat_fontname_as_pattern;
double Blur;
int Justify;
} ASS_Style;
/*
* ASS_Event corresponds to a single Dialogue line;
* text is stored as-is, style overrides will be parsed later.
*/
typedef struct ass_event {
long long Start; // ms
long long Duration; // ms
int ReadOrder;
int Layer;
int Style;
char *Name;
int MarginL;
int MarginR;
int MarginV;
char *Effect;
char *Text;
ASS_RenderPriv *render_priv;
} ASS_Event;
/**
* Support for (xy-)vsfilter mangled colors
*
* Generally, xy-vsfilter emulates the classic vsfilter behavior of
* rendering directly into the (usually YCbCr) video. vsfilter is
* hardcoded to use BT.601(TV) as target colorspace when converting
* the subtitle RGB color to the video colorspace. This led to major
* breakage when HDTV video was introduced: HDTV typically uses
* BT.709(TV), but vsfilter still used BT.601(TV) for conversion.
*
* This means classic vsfilter will mangle colors as follows:
*
* screen_rgb = bt_709tv_to_rgb(rgb_to_bt601tv(ass_rgb))
*
* Or in general:
*
* screen_rgb = video_csp_to_rgb(rgb_to_bt601tv(ass_rgb))
*
* where video_csp is the colorspace of the video with which the
* subtitle was muxed.
*
* xy-vsfilter did not fix this, but instead introduced explicit
* rules how colors were mangled by adding a "YCbCr Matrix" header.
* If this header specifies a real colorspace (like BT.601(TV) etc.),
* xy-vsfilter behaves exactly like vsfilter, but using the specified
* colorspace for conversion of ASS input RGB to screen RGB:
*
* screen_rgb = video_csp_to_rgb(rgb_to_ycbcr_header_csp(ass_rgb))
*
* Further, xy-vsfilter behaves like vsfilter with no changes if the header
* is missing.
*
* The special value "None" means untouched RGB values. Keep in mind that
* some version of xy-vsfilter are buggy and don't interpret this correctly.
* It appears some people are advocating that this header value is
* intended for situations where exact colors do not matter.
*
* Note that newer Aegisub versions (the main application to produce ASS
* subtitle scripts) have an option that tries not to mangle the colors. It
* is said that if the header is not set to BT.601(TV), the colors are
* supposed not to be mangled, even if the "YCbCr Matrix" header is not
* set to "None". In other words, the video colorspace as detected by
* Aegisub is the same as identified in the file header.
*
* In general, misinterpreting this header or not using it will lead to
* slightly different subtitle colors, which can matter if the subtitle
* attempts to match solid colored areas in the video.
*
* Note that libass doesn't change colors based on this header. It
* absolutely can't do that, because the video colorspace is required
* in order to handle this as intended by xy-vsfilter.
*/
typedef enum ASS_YCbCrMatrix {
YCBCR_DEFAULT = 0, // Header missing
YCBCR_UNKNOWN, // Header could not be parsed correctly
YCBCR_NONE, // "None" special value
YCBCR_BT601_TV,
YCBCR_BT601_PC,
YCBCR_BT709_TV,
YCBCR_BT709_PC,
YCBCR_SMPTE240M_TV,
YCBCR_SMPTE240M_PC,
YCBCR_FCC_TV,
YCBCR_FCC_PC
} ASS_YCbCrMatrix;
/*
* ass track represent either an external script or a matroska subtitle stream
* (no real difference between them); it can be used in rendering after the
* headers are parsed (i.e. events format line read).
*/
typedef struct ass_track {
int n_styles; // amount used
int max_styles; // amount allocated
int n_events;
int max_events;
ASS_Style *styles; // array of styles, max_styles length, n_styles used
ASS_Event *events; // the same as styles
char *style_format; // style format line (everything after "Format: ")
char *event_format; // event format line
enum {
TRACK_TYPE_UNKNOWN = 0,
TRACK_TYPE_ASS,
TRACK_TYPE_SSA
} track_type;
// Script header fields
int PlayResX;
int PlayResY;
double Timer;
int WrapStyle;
int ScaledBorderAndShadow;
int Kerning;
char *Language;
ASS_YCbCrMatrix YCbCrMatrix;
int default_style; // index of default style
char *name; // file name in case of external subs, 0 for streams
ASS_Library *library;
ASS_ParserPriv *parser_priv;
} ASS_Track;
#endif /* LIBASS_TYPES_H */

532
libass/ass_utils.c Normal file
View File

@ -0,0 +1,532 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "ass_compat.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <inttypes.h>
#include "ass_library.h"
#include "ass.h"
#include "ass_utils.h"
#include "ass_string.h"
#if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
#include "x86/cpuid.h"
int has_sse2(void)
{
uint32_t eax = 1, ebx, ecx, edx;
ass_get_cpuid(&eax, &ebx, &ecx, &edx);
return (edx >> 26) & 0x1;
}
int has_avx(void)
{
uint32_t eax = 1, ebx, ecx, edx;
ass_get_cpuid(&eax, &ebx, &ecx, &edx);
if(!(ecx & (1 << 27))) // not OSXSAVE
return 0;
uint32_t misc = ecx;
ass_get_xgetbv(0, &eax, &edx);
if((eax & 0x6) != 0x6)
return 0;
eax = 0;
ass_get_cpuid(&eax, &ebx, &ecx, &edx);
return (ecx & 0x6) == 0x6 ? (misc >> 28) & 0x1 : 0; // check high bits are relevant, then AVX support
}
int has_avx2(void)
{
uint32_t eax = 7, ebx, ecx, edx;
ass_get_cpuid(&eax, &ebx, &ecx, &edx);
return (ebx >> 5) & has_avx();
}
#endif // ASM
#ifndef HAVE_STRNDUP
char *ass_strndup(const char *s, size_t n)
{
char *end = memchr(s, 0, n);
size_t len = end ? end - s : n;
char *new = len < SIZE_MAX ? malloc(len + 1) : NULL;
if (new) {
memcpy(new, s, len);
new[len] = 0;
}
return new;
}
#endif
void *ass_aligned_alloc(size_t alignment, size_t size, bool zero)
{
assert(!(alignment & (alignment - 1))); // alignment must be power of 2
if (size >= SIZE_MAX - alignment - sizeof(void *))
return NULL;
char *allocation = zero ? calloc(size + sizeof(void *) + alignment - 1, 1)
: malloc(size + sizeof(void *) + alignment - 1);
if (!allocation)
return NULL;
char *ptr = allocation + sizeof(void *);
unsigned int misalign = (uintptr_t)ptr & (alignment - 1);
if (misalign)
ptr += alignment - misalign;
*((void **)ptr - 1) = allocation;
return ptr;
}
void ass_aligned_free(void *ptr)
{
if (ptr)
free(*((void **)ptr - 1));
}
/**
* This works similar to realloc(ptr, nmemb * size), but checks for overflow.
*
* Unlike some implementations of realloc, this never acts as a call to free().
* If the total size is 0, it is bumped up to 1. This means a NULL return always
* means allocation failure, and the unportable realloc(0, 0) case is avoided.
*/
void *ass_realloc_array(void *ptr, size_t nmemb, size_t size)
{
if (nmemb > (SIZE_MAX / size))
return NULL;
size *= nmemb;
if (size < 1)
size = 1;
return realloc(ptr, size);
}
/**
* Like ass_realloc_array(), but:
* 1. on failure, return the original ptr value, instead of NULL
* 2. set errno to indicate failure (errno!=0) or success (errno==0)
*/
void *ass_try_realloc_array(void *ptr, size_t nmemb, size_t size)
{
void *new_ptr = ass_realloc_array(ptr, nmemb, size);
if (new_ptr) {
errno = 0;
return new_ptr;
} else {
errno = ENOMEM;
return ptr;
}
}
void skip_spaces(char **str)
{
char *p = *str;
while ((*p == ' ') || (*p == '\t'))
++p;
*str = p;
}
void rskip_spaces(char **str, char *limit)
{
char *p = *str;
while ((p > limit) && ((p[-1] == ' ') || (p[-1] == '\t')))
--p;
*str = p;
}
int mystrtoi(char **p, int *res)
{
char *start = *p;
double temp_res = ass_strtod(*p, p);
*res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
return *p != start;
}
int mystrtoll(char **p, long long *res)
{
char *start = *p;
double temp_res = ass_strtod(*p, p);
*res = (long long) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
return *p != start;
}
int mystrtod(char **p, double *res)
{
char *start = *p;
*res = ass_strtod(*p, p);
return *p != start;
}
int mystrtoi32(char **p, int base, int32_t *res)
{
char *start = *p;
long long temp_res = strtoll(*p, p, base);
*res = FFMINMAX(temp_res, INT32_MIN, INT32_MAX);
return *p != start;
}
static int read_digits(char **str, int base, uint32_t *res)
{
char *p = *str;
char *start = p;
uint32_t val = 0;
while (1) {
int digit;
if (*p >= '0' && *p < FFMIN(base, 10) + '0')
digit = *p - '0';
else if (*p >= 'a' && *p < base - 10 + 'a')
digit = *p - 'a' + 10;
else if (*p >= 'A' && *p < base - 10 + 'A')
digit = *p - 'A' + 10;
else
break;
val = val * base + digit;
++p;
}
*res = val;
*str = p;
return p != start;
}
/**
* \brief Convert a string to an integer reduced modulo 2**32
* Follows the rules for strtoul but reduces the number modulo 2**32
* instead of saturating it to 2**32 - 1.
*/
static int mystrtou32_modulo(char **p, int base, uint32_t *res)
{
// This emulates scanf with %d or %x format as it works on
// Windows, because that's what is used by VSFilter. In practice,
// scanf works the same way on other platforms too, but
// the standard leaves its behavior on overflow undefined.
// Unlike scanf and like strtoul, produce 0 for invalid inputs.
char *start = *p;
int sign = 1;
skip_spaces(p);
if (**p == '+')
++*p;
else if (**p == '-')
sign = -1, ++*p;
if (base == 16 && !ass_strncasecmp(*p, "0x", 2))
*p += 2;
if (read_digits(p, base, res)) {
*res *= sign;
return 1;
} else {
*p = start;
return 0;
}
}
int32_t parse_alpha_tag(char *str)
{
int32_t alpha = 0;
while (*str == '&' || *str == 'H')
++str;
mystrtoi32(&str, 16, &alpha);
return alpha;
}
uint32_t parse_color_tag(char *str)
{
int32_t color = 0;
while (*str == '&' || *str == 'H')
++str;
mystrtoi32(&str, 16, &color);
return ass_bswap32((uint32_t) color);
}
uint32_t parse_color_header(char *str)
{
uint32_t color = 0;
int base;
if (!ass_strncasecmp(str, "&h", 2) || !ass_strncasecmp(str, "0x", 2)) {
str += 2;
base = 16;
} else
base = 10;
mystrtou32_modulo(&str, base, &color);
return ass_bswap32(color);
}
// Return a boolean value for a string
char parse_bool(char *str)
{
skip_spaces(&str);
return !ass_strncasecmp(str, "yes", 3) || strtol(str, NULL, 10) > 0;
}
int parse_ycbcr_matrix(char *str)
{
skip_spaces(&str);
if (*str == '\0')
return YCBCR_DEFAULT;
char *end = str + strlen(str);
rskip_spaces(&end, str);
// Trim a local copy of the input that we know is safe to
// modify. The buffer is larger than any valid string + NUL,
// so we can simply chop off the rest of the input.
char buffer[16];
size_t n = FFMIN(end - str, sizeof buffer - 1);
memcpy(buffer, str, n);
buffer[n] = '\0';
if (!ass_strcasecmp(buffer, "none"))
return YCBCR_NONE;
if (!ass_strcasecmp(buffer, "tv.601"))
return YCBCR_BT601_TV;
if (!ass_strcasecmp(buffer, "pc.601"))
return YCBCR_BT601_PC;
if (!ass_strcasecmp(buffer, "tv.709"))
return YCBCR_BT709_TV;
if (!ass_strcasecmp(buffer, "pc.709"))
return YCBCR_BT709_PC;
if (!ass_strcasecmp(buffer, "tv.240m"))
return YCBCR_SMPTE240M_TV;
if (!ass_strcasecmp(buffer, "pc.240m"))
return YCBCR_SMPTE240M_PC;
if (!ass_strcasecmp(buffer, "tv.fcc"))
return YCBCR_FCC_TV;
if (!ass_strcasecmp(buffer, "pc.fcc"))
return YCBCR_FCC_PC;
return YCBCR_UNKNOWN;
}
/**
* \brief converts numpad-style align to align.
*/
int numpad2align(int val)
{
if (val < -INT_MAX)
// Pick an alignment somewhat arbitrarily. VSFilter handles
// INT32_MIN as a mix of 1, 2 and 3, so prefer one of those values.
val = 2;
else if (val < 0)
val = -val;
int res = ((val - 1) % 3) + 1; // horizontal alignment
if (val <= 3)
res |= VALIGN_SUB;
else if (val <= 6)
res |= VALIGN_CENTER;
else
res |= VALIGN_TOP;
return res;
}
void ass_msg(ASS_Library *priv, int lvl, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
priv->msg_callback(lvl, fmt, va, priv->msg_callback_data);
va_end(va);
}
unsigned ass_utf8_get_char(char **str)
{
uint8_t *strp = (uint8_t *) * str;
unsigned c = *strp++;
unsigned mask = 0x80;
int len = -1;
while (c & mask) {
mask >>= 1;
len++;
}
if (len <= 0 || len > 4)
goto no_utf8;
c &= mask - 1;
while ((*strp & 0xc0) == 0x80) {
if (len-- <= 0)
goto no_utf8;
c = (c << 6) | (*strp++ & 0x3f);
}
if (len)
goto no_utf8;
*str = (char *) strp;
return c;
no_utf8:
strp = (uint8_t *) * str;
c = *strp++;
*str = (char *) strp;
return c;
}
/**
* Original version from http://www.cprogramming.com/tutorial/utf8.c
* \brief Converts a single UTF-32 code point to UTF-8
* \param dest Buffer to write to. Writes a NULL terminator.
* \param ch 32-bit character code to convert
* \return number of bytes written
* converts a single character and ASSUMES YOU HAVE ENOUGH SPACE
*/
unsigned ass_utf8_put_char(char *dest, uint32_t ch)
{
char *orig_dest = dest;
if (ch < 0x80) {
*dest++ = (char)ch;
} else if (ch < 0x800) {
*dest++ = (ch >> 6) | 0xC0;
*dest++ = (ch & 0x3F) | 0x80;
} else if (ch < 0x10000) {
*dest++ = (ch >> 12) | 0xE0;
*dest++ = ((ch >> 6) & 0x3F) | 0x80;
*dest++ = (ch & 0x3F) | 0x80;
} else if (ch < 0x110000) {
*dest++ = (ch >> 18) | 0xF0;
*dest++ = ((ch >> 12) & 0x3F) | 0x80;
*dest++ = ((ch >> 6) & 0x3F) | 0x80;
*dest++ = (ch & 0x3F) | 0x80;
}
*dest = '\0';
return dest - orig_dest;
}
/**
* \brief Parse UTF-16 and return the code point of the sequence starting at src.
* \param src pointer to a pointer to the start of the UTF-16 data
* (will be set to the start of the next code point)
* \return the code point
*/
static uint32_t ass_read_utf16be(uint8_t **src, size_t bytes)
{
if (bytes < 2)
goto too_short;
uint32_t cp = ((*src)[0] << 8) | (*src)[1];
*src += 2;
bytes -= 2;
if (cp >= 0xD800 && cp <= 0xDBFF) {
if (bytes < 2)
goto too_short;
uint32_t cp2 = ((*src)[0] << 8) | (*src)[1];
if (cp2 < 0xDC00 || cp2 > 0xDFFF)
return 0xFFFD;
*src += 2;
cp = 0x10000 + ((cp - 0xD800) << 10) + (cp2 - 0xDC00);
}
if (cp >= 0xDC00 && cp <= 0xDFFF)
return 0xFFFD;
return cp;
too_short:
*src += bytes;
return 0xFFFD;
}
void ass_utf16be_to_utf8(char *dst, size_t dst_size, uint8_t *src, size_t src_size)
{
uint8_t *end = src + src_size;
if (!dst_size)
return;
while (src < end) {
uint32_t cp = ass_read_utf16be(&src, end - src);
if (dst_size < 5)
break;
unsigned s = ass_utf8_put_char(dst, cp);
dst += s;
dst_size -= s;
}
*dst = '\0';
}
/**
* \brief find style by name
* \param track track
* \param name style name
* \return index in track->styles
* Returns 0 if no styles found => expects at least 1 style.
* Parsing code always adds "Default" style in the beginning.
*/
int lookup_style(ASS_Track *track, char *name)
{
int i;
// '*' seem to mean literally nothing;
// VSFilter removes them as soon as it can
while (*name == '*')
++name;
// VSFilter then normalizes the case of "Default"
// (only in contexts where this function is called)
if (ass_strcasecmp(name, "Default") == 0)
name = "Default";
for (i = track->n_styles - 1; i >= 0; --i) {
if (strcmp(track->styles[i].Name, name) == 0)
return i;
}
i = track->default_style;
ass_msg(track->library, MSGL_WARN,
"[%p]: Warning: no style named '%s' found, using '%s'",
track, name, track->styles[i].Name);
return i;
}
/**
* \brief find style by name as in \r
* \param track track
* \param name style name
* \param len style name length
* \return style in track->styles
* Returns NULL if no style has the given name.
*/
ASS_Style *lookup_style_strict(ASS_Track *track, char *name, size_t len)
{
int i;
for (i = track->n_styles - 1; i >= 0; --i) {
if (strncmp(track->styles[i].Name, name, len) == 0 &&
track->styles[i].Name[len] == '\0')
return track->styles + i;
}
ass_msg(track->library, MSGL_WARN,
"[%p]: Warning: no style named '%.*s' found",
track, (int) len, name);
return NULL;
}

201
libass/ass_utils.h Normal file
View File

@ -0,0 +1,201 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_UTILS_H
#define LIBASS_UTILS_H
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <math.h>
#include "ass.h"
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
#define MSGL_FATAL 0
#define MSGL_ERR 1
#define MSGL_WARN 2
#define MSGL_INFO 4
#define MSGL_V 6
#define MSGL_DBG2 7
#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
#define FFMINMAX(c,a,b) FFMIN(FFMAX(c, a), b)
#if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
int has_sse2(void);
int has_avx(void);
int has_avx2(void);
#endif
#ifndef HAVE_STRNDUP
char *ass_strndup(const char *s, size_t n);
#define strndup ass_strndup
#endif
void *ass_aligned_alloc(size_t alignment, size_t size, bool zero);
void ass_aligned_free(void *ptr);
void *ass_realloc_array(void *ptr, size_t nmemb, size_t size);
void *ass_try_realloc_array(void *ptr, size_t nmemb, size_t size);
/**
* Reallocate the array in ptr to at least count elements. For example, if
* you do "int *ptr = NULL; ASS_REALLOC_ARRAY(ptr, 5)", you can access ptr[0]
* through ptr[4] (inclusive).
*
* If memory allocation fails, ptr is left unchanged, and the macro returns 0:
* "if (!ASS_REALLOC_ARRAY(ptr, 5)) goto error;"
*
* A count of 0 does not free the array (see ass_realloc_array for remarks).
*/
#define ASS_REALLOC_ARRAY(ptr, count) \
(errno = 0, (ptr) = ass_try_realloc_array(ptr, count, sizeof(*ptr)), !errno)
void skip_spaces(char **str);
void rskip_spaces(char **str, char *limit);
int mystrtoi(char **p, int *res);
int mystrtoll(char **p, long long *res);
int mystrtod(char **p, double *res);
int mystrtoi32(char **p, int base, int32_t *res);
int32_t parse_alpha_tag(char *str);
uint32_t parse_color_tag(char *str);
uint32_t parse_color_header(char *str);
char parse_bool(char *str);
int parse_ycbcr_matrix(char *str);
int numpad2align(int val);
unsigned ass_utf8_get_char(char **str);
unsigned ass_utf8_put_char(char *dest, uint32_t ch);
void ass_utf16be_to_utf8(char *dst, size_t dst_size, uint8_t *src, size_t src_size);
void ass_msg(ASS_Library *priv, int lvl, const char *fmt, ...);
int lookup_style(ASS_Track *track, char *name);
ASS_Style *lookup_style_strict(ASS_Track *track, char *name, size_t len);
/* defined in ass_strtod.c */
double ass_strtod(const char *string, char **endPtr);
static inline size_t ass_align(size_t alignment, size_t s)
{
if (s > SIZE_MAX - (alignment - 1))
return s;
return (s + (alignment - 1)) & ~(alignment - 1);
}
static inline uint32_t ass_bswap32(uint32_t x)
{
#ifdef _MSC_VER
return _byteswap_ulong(x);
#else
return (x & 0x000000FF) << 24 | (x & 0x0000FF00) << 8 |
(x & 0x00FF0000) >> 8 | (x & 0xFF000000) >> 24;
#endif
}
static inline int d6_to_int(int x)
{
return (x + 32) >> 6;
}
static inline int d16_to_int(int x)
{
return (x + 32768) >> 16;
}
static inline int int_to_d6(int x)
{
return x * (1 << 6);
}
static inline int int_to_d16(int x)
{
return x * (1 << 16);
}
static inline int d16_to_d6(int x)
{
return (x + 512) >> 10;
}
static inline int d6_to_d16(int x)
{
return x * (1 << 10);
}
static inline double d6_to_double(int x)
{
return x / 64.;
}
static inline int double_to_d6(double x)
{
return (int) (x * 64);
}
static inline double d16_to_double(int x)
{
return ((double) x) / 0x10000;
}
static inline int double_to_d16(double x)
{
return (int) (x * 0x10000);
}
static inline double d22_to_double(int x)
{
return ((double) x) / 0x400000;
}
static inline int double_to_d22(double x)
{
return (int) (x * 0x400000);
}
// Calculate cache key for a rotational angle in radians
static inline int rot_key(double a)
{
return double_to_d22(remainder(a, 2 * M_PI));
}
#define FNV1_32A_INIT 0x811c9dc5U
#define FNV1_32A_PRIME 16777619U
static inline unsigned fnv_32a_buf(void *buf, size_t len, unsigned hval)
{
unsigned char *bp = (unsigned char*)buf;
size_t n = (len + 3) / 4;
switch (len % 4) {
case 0: do { hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
case 3: hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
case 2: hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
case 1: hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
} while (--n > 0);
}
return hval;
}
static inline unsigned fnv_32a_str(char *str, unsigned hval)
{
unsigned char *s = (unsigned char *) str;
while (*s) {
hval ^= (unsigned) *s++;
hval *= FNV1_32A_PRIME;
}
return hval;
}
#endif /* LIBASS_UTILS_H */

687
libass/dwrite_c.h Normal file
View File

@ -0,0 +1,687 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
/**
* Stripped version. Only definitions needed by libass. Contains fixes to
* make it compile with C. Also needed on MSVC.
*/
#ifndef __INC_DWRITE__
#define __INC_DWRITE__
#define DWRITEAPI DECLSPEC_IMPORT
#include <unknwn.h>
typedef struct IDWriteFactory IDWriteFactory;
typedef struct IDWriteFont IDWriteFont;
typedef struct IDWriteFontCollection IDWriteFontCollection;
typedef struct IDWriteFontFace IDWriteFontFace;
typedef struct IDWriteFontFamily IDWriteFontFamily;
typedef struct IDWriteFontList IDWriteFontList;
typedef struct IDWriteFontFile IDWriteFontFile;
typedef struct IDWriteFontFileLoader IDWriteFontFileLoader;
typedef struct IDWriteFontFileStream IDWriteFontFileStream;
typedef struct IDWriteInlineObject IDWriteInlineObject;
typedef struct IDWriteLocalizedStrings IDWriteLocalizedStrings;
typedef struct IDWritePixelSnapping IDWritePixelSnapping;
typedef struct IDWriteTextFormat IDWriteTextFormat;
typedef struct IDWriteTextLayout IDWriteTextLayout;
typedef struct IDWriteTextRenderer IDWriteTextRenderer;
#include <dcommon.h>
typedef enum DWRITE_INFORMATIONAL_STRING_ID {
DWRITE_INFORMATIONAL_STRING_NONE = 0,
DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE,
DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS,
DWRITE_INFORMATIONAL_STRING_TRADEMARK,
DWRITE_INFORMATIONAL_STRING_MANUFACTURER,
DWRITE_INFORMATIONAL_STRING_DESIGNER,
DWRITE_INFORMATIONAL_STRING_DESIGNER_URL,
DWRITE_INFORMATIONAL_STRING_DESCRIPTION,
DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL,
DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION,
DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL,
DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES,
DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES,
DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES,
DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES,
DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT,
DWRITE_INFORMATIONAL_STRING_FULL_NAME,
DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME,
DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME
} DWRITE_INFORMATIONAL_STRING_ID;
typedef enum DWRITE_FACTORY_TYPE {
DWRITE_FACTORY_TYPE_SHARED = 0,
DWRITE_FACTORY_TYPE_ISOLATED
} DWRITE_FACTORY_TYPE;
typedef enum DWRITE_FONT_FACE_TYPE {
DWRITE_FONT_FACE_TYPE_CFF = 0,
DWRITE_FONT_FACE_TYPE_TRUETYPE,
DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION,
DWRITE_FONT_FACE_TYPE_TYPE1,
DWRITE_FONT_FACE_TYPE_VECTOR,
DWRITE_FONT_FACE_TYPE_BITMAP,
DWRITE_FONT_FACE_TYPE_UNKNOWN,
DWRITE_FONT_FACE_TYPE_RAW_CFF
} DWRITE_FONT_FACE_TYPE;
typedef enum DWRITE_FONT_SIMULATIONS {
DWRITE_FONT_SIMULATIONS_NONE = 0x0000,
DWRITE_FONT_SIMULATIONS_BOLD = 0x0001,
DWRITE_FONT_SIMULATIONS_OBLIQUE = 0x0002
} DWRITE_FONT_SIMULATIONS;
typedef enum DWRITE_FONT_STRETCH {
DWRITE_FONT_STRETCH_UNDEFINED = 0,
DWRITE_FONT_STRETCH_ULTRA_CONDENSED = 1,
DWRITE_FONT_STRETCH_EXTRA_CONDENSED = 2,
DWRITE_FONT_STRETCH_CONDENSED = 3,
DWRITE_FONT_STRETCH_SEMI_CONDENSED = 4,
DWRITE_FONT_STRETCH_NORMAL = 5,
DWRITE_FONT_STRETCH_MEDIUM = 5,
DWRITE_FONT_STRETCH_SEMI_EXPANDED = 6,
DWRITE_FONT_STRETCH_EXPANDED = 7,
DWRITE_FONT_STRETCH_EXTRA_EXPANDED = 8,
DWRITE_FONT_STRETCH_ULTRA_EXPANDED = 9
} DWRITE_FONT_STRETCH;
typedef enum DWRITE_FONT_STYLE {
DWRITE_FONT_STYLE_NORMAL = 0,
DWRITE_FONT_STYLE_OBLIQUE,
DWRITE_FONT_STYLE_ITALIC
} DWRITE_FONT_STYLE;
typedef enum DWRITE_FONT_WEIGHT {
DWRITE_FONT_WEIGHT_MEDIUM = 500,
/* rest dropped */
} DWRITE_FONT_WEIGHT;
typedef struct DWRITE_FONT_METRICS {
UINT16 designUnitsPerEm;
UINT16 ascent;
UINT16 descent;
INT16 lineGap;
UINT16 capHeight;
UINT16 xHeight;
INT16 underlinePosition;
UINT16 underlineThickness;
INT16 strikethroughPosition;
UINT16 strikethroughThickness;
} DWRITE_FONT_METRICS;
typedef struct DWRITE_GLYPH_OFFSET DWRITE_GLYPH_OFFSET;
typedef struct DWRITE_GLYPH_RUN {
IDWriteFontFace *fontFace;
FLOAT fontEmSize;
UINT32 glyphCount;
const UINT16 *glyphIndices;
const FLOAT *glyphAdvances;
const DWRITE_GLYPH_OFFSET *glyphOffsets;
BOOL isSideways;
UINT32 bidiLevel;
} DWRITE_GLYPH_RUN;
typedef struct DWRITE_GLYPH_RUN_DESCRIPTION DWRITE_GLYPH_RUN_DESCRIPTION;
typedef struct DWRITE_HIT_TEST_METRICS DWRITE_HIT_TEST_METRICS;
typedef struct DWRITE_LINE_METRICS DWRITE_LINE_METRICS;
typedef struct DWRITE_MATRIX DWRITE_MATRIX;
typedef struct DWRITE_STRIKETHROUGH DWRITE_STRIKETHROUGH;
typedef struct DWRITE_TEXT_METRICS DWRITE_TEXT_METRICS;
typedef struct DWRITE_TEXT_RANGE {
UINT32 startPosition;
UINT32 length;
} DWRITE_TEXT_RANGE;
typedef struct DWRITE_TRIMMING DWRITE_TRIMMING;
typedef struct DWRITE_UNDERLINE DWRITE_UNDERLINE;
#ifndef __MINGW_DEF_ARG_VAL
#ifdef __cplusplus
#define __MINGW_DEF_ARG_VAL(x) = x
#else
#define __MINGW_DEF_ARG_VAL(x)
#endif
#endif
#undef INTERFACE
#define INTERFACE IDWriteFactory
DECLARE_INTERFACE_(IDWriteFactory,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteFactory methods */
STDMETHOD(GetSystemFontCollection)(THIS_
IDWriteFontCollection **fontCollection,
BOOL checkForUpdates __MINGW_DEF_ARG_VAL(FALSE)) PURE;
STDMETHOD(dummy1)(THIS);
STDMETHOD(dummy2)(THIS);
STDMETHOD(dummy3)(THIS);
STDMETHOD(dummy4)(THIS);
STDMETHOD(dummy5)(THIS);
STDMETHOD(dummy6)(THIS);
STDMETHOD(dummy7)(THIS);
STDMETHOD(dummy8)(THIS);
STDMETHOD(dummy9)(THIS);
STDMETHOD(dummy10)(THIS);
STDMETHOD(dummy11)(THIS);
STDMETHOD(CreateTextFormat)(THIS_
WCHAR const *fontFamilyName,
IDWriteFontCollection *fontCollection,
DWRITE_FONT_WEIGHT fontWeight,
DWRITE_FONT_STYLE fontStyle,
DWRITE_FONT_STRETCH fontStretch,
FLOAT fontSize,
WCHAR const *localeName,
IDWriteTextFormat **textFormat) PURE;
STDMETHOD(dummy12)(THIS);
STDMETHOD(dummy13)(THIS);
STDMETHOD(CreateTextLayout)(THIS_
WCHAR const *string,
UINT32 stringLength,
IDWriteTextFormat *textFormat,
FLOAT maxWidth,
FLOAT maxHeight,
IDWriteTextLayout **textLayout) PURE;
/* remainder dropped */
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFactory_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
#define IDWriteFactory_AddRef(This) (This)->lpVtbl->AddRef(This)
#define IDWriteFactory_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFactory_GetSystemFontCollection(This,fontCollection,checkForUpdates) (This)->lpVtbl->GetSystemFontCollection(This,fontCollection,checkForUpdates)
#define IDWriteFactory_CreateTextFormat(This,fontFamilyName,fontCollection,fontWeight,fontStyle,fontStretch,fontSize,localeName,textFormat) (This)->lpVtbl->CreateTextFormat(This,fontFamilyName,fontCollection,fontWeight,fontStyle,fontStretch,fontSize,localeName,textFormat)
#define IDWriteFactory_CreateTextLayout(This,string,stringLength,textFormat,maxWidth,maxHeight,textLayout) (This)->lpVtbl->CreateTextLayout(This,string,stringLength,textFormat,maxWidth,maxHeight,textLayout)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteFont
DECLARE_INTERFACE_(IDWriteFont,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteFont methods */
STDMETHOD(GetFontFamily)(THIS_
IDWriteFontFamily **fontFamily) PURE;
STDMETHOD_(DWRITE_FONT_WEIGHT, GetWeight)(THIS) PURE;
STDMETHOD_(DWRITE_FONT_STRETCH, GetStretch)(THIS) PURE;
STDMETHOD_(DWRITE_FONT_STYLE, GetStyle)(THIS) PURE;
STDMETHOD_(BOOL, IsSymbolFont)(THIS) PURE;
STDMETHOD(GetFaceNames)(THIS_
IDWriteLocalizedStrings **names) PURE;
STDMETHOD(GetInformationalStrings)(THIS_
DWRITE_INFORMATIONAL_STRING_ID informationalStringID,
IDWriteLocalizedStrings **informationalStrings,
BOOL *exists) PURE;
STDMETHOD_(DWRITE_FONT_SIMULATIONS, GetSimulations)(THIS) PURE;
STDMETHOD_(void, GetMetrics)(THIS_
DWRITE_FONT_METRICS *fontMetrics) PURE;
STDMETHOD(HasCharacter)(THIS_
UINT32 unicodeValue,
BOOL *exists) PURE;
STDMETHOD(CreateFontFace)(THIS_
IDWriteFontFace **fontFace) PURE;
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFont_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
#define IDWriteFont_AddRef(This) (This)->lpVtbl->AddRef(This)
#define IDWriteFont_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFont_CreateFontFace(This,fontFace) (This)->lpVtbl->CreateFontFace(This,fontFace)
#define IDWriteFont_GetFaceNames(This,names) (This)->lpVtbl->GetFaceNames(This,names)
#define IDWriteFont_GetFontFamily(This,fontFamily) (This)->lpVtbl->GetFontFamily(This,fontFamily)
#define IDWriteFont_GetInformationalStrings(This,informationalStringID,informationalStrings,exists) (This)->lpVtbl->GetInformationalStrings(This,informationalStringID,informationalStrings,exists)
#define IDWriteFont_GetMetrics(This,fontMetrics) (This)->lpVtbl->GetMetrics(This,fontMetrics)
#define IDWriteFont_GetSimulations(This) (This)->lpVtbl->GetSimulations(This)
#define IDWriteFont_GetStretch(This) (This)->lpVtbl->GetStretch(This)
#define IDWriteFont_GetStyle(This) (This)->lpVtbl->GetStyle(This)
#define IDWriteFont_GetWeight(This) (This)->lpVtbl->GetWeight(This)
#define IDWriteFont_HasCharacter(This,unicodeValue,exists) (This)->lpVtbl->HasCharacter(This,unicodeValue,exists)
#define IDWriteFont_IsSymbolFont(This) (This)->lpVtbl->IsSymbolFont(This)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteFontCollection
DECLARE_INTERFACE_(IDWriteFontCollection,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteFontCollection methods */
STDMETHOD_(UINT32, GetFontFamilyCount)(THIS) PURE;
STDMETHOD(GetFontFamily)(THIS_
UINT32 index,
IDWriteFontFamily **fontFamily) PURE;
STDMETHOD(FindFamilyName)(THIS_
WCHAR const *familyName,
UINT32 *index,
BOOL *exists) PURE;
STDMETHOD(GetFontFromFontFace)(THIS_
IDWriteFontFace* fontFace,
IDWriteFont **font) PURE;
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFontCollection_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
#define IDWriteFontCollection_AddRef(This) (This)->lpVtbl->AddRef(This)
#define IDWriteFontCollection_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFontCollection_FindFamilyName(This,familyName,index,exists) (This)->lpVtbl->FindFamilyName(This,familyName,index,exists)
#define IDWriteFontCollection_GetFontFamily(This,index,fontFamily) (This)->lpVtbl->GetFontFamily(This,index,fontFamily)
#define IDWriteFontCollection_GetFontFamilyCount(This) (This)->lpVtbl->GetFontFamilyCount(This)
#define IDWriteFontCollection_GetFontFromFontFace(This,fontFace,font) (This)->lpVtbl->GetFontFromFontFace(This,fontFace,font)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteFontFace
DECLARE_INTERFACE_(IDWriteFontFace,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteFontFace methods */
STDMETHOD_(DWRITE_FONT_FACE_TYPE, GetType)(THIS) PURE;
STDMETHOD(GetFiles)(THIS_
UINT32 *numberOfFiles,
IDWriteFontFile **fontFiles) PURE;
STDMETHOD_(UINT32, GetIndex)(THIS) PURE;
/* rest dropped */
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFontFace_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFontFace_GetType(This) (This)->lpVtbl->GetType(This)
#define IDWriteFontFace_GetFiles(This,fontFiles,b) (This)->lpVtbl->GetFiles(This,fontFiles,b)
#define IDWriteFontFace_GetIndex(This) (This)->lpVtbl->GetIndex(This)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteFontFamily
DECLARE_INTERFACE_(IDWriteFontFamily,IDWriteFontList)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
/* IDWriteFontList methods */
STDMETHOD(GetFontCollection)(THIS_
IDWriteFontCollection** fontCollection) PURE;
STDMETHOD_(UINT32, GetFontCount)(THIS) PURE;
STDMETHOD(GetFont)(THIS_
UINT32 index,
IDWriteFont **font) PURE;
#endif
/* IDWriteFontFamily methods */
STDMETHOD(GetFamilyNames)(THIS_
IDWriteLocalizedStrings **names) PURE;
/* rest dropped */
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFontFamily_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
#define IDWriteFontFamily_AddRef(This) (This)->lpVtbl->AddRef(This)
#define IDWriteFontFamily_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFontFamily_GetFont(This,index,font) (This)->lpVtbl->GetFont(This,index,font)
#define IDWriteFontFamily_GetFontCount(This) (This)->lpVtbl->GetFontCount(This)
#define IDWriteFontFamily_GetFamilyNames(This,names) (This)->lpVtbl->GetFamilyNames(This,names)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteFontFile
DECLARE_INTERFACE_(IDWriteFontFile,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteFontFile methods */
STDMETHOD(GetReferenceKey)(THIS_
void const **fontFileReferenceKey,
UINT32 *fontFileReferenceKeySize) PURE;
STDMETHOD(GetLoader)(THIS_
IDWriteFontFileLoader **fontFileLoader) PURE;
/* rest dropped */
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFontFile_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFontFile_GetLoader(This,fontFileLoader) (This)->lpVtbl->GetLoader(This,fontFileLoader)
#define IDWriteFontFile_GetReferenceKey(This,fontFileReferenceKey,fontFileReferenceKeySize) (This)->lpVtbl->GetReferenceKey(This,fontFileReferenceKey,fontFileReferenceKeySize)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteFontFileLoader
DECLARE_INTERFACE_(IDWriteFontFileLoader,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteFontFileLoader methods */
STDMETHOD(CreateStreamFromKey)(THIS_
void const *fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
IDWriteFontFileStream **fontFileStream) PURE;
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFontFileLoader_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
#define IDWriteFontFileLoader_AddRef(This) (This)->lpVtbl->AddRef(This)
#define IDWriteFontFileLoader_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFontFileLoader_CreateStreamFromKey(This,fontFileReferenceKey,fontFileReferenceKeySize,fontFileStream) (This)->lpVtbl->CreateStreamFromKey(This,fontFileReferenceKey,fontFileReferenceKeySize,fontFileStream)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteFontFileStream
DECLARE_INTERFACE_(IDWriteFontFileStream,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteFontFileStream methods */
STDMETHOD(ReadFileFragment)(THIS_
void const **fragmentStart,
UINT64 fileOffset,
UINT64 fragmentSize,
void** fragmentContext) PURE;
STDMETHOD_(void, ReleaseFileFragment)(THIS_
void *fragmentContext) PURE;
STDMETHOD(GetFileSize)(THIS_
UINT64 *fileSize) PURE;
STDMETHOD(GetLastWriteTime)(THIS_
UINT64 *lastWriteTime) PURE;
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteFontFileStream_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
#define IDWriteFontFileStream_AddRef(This) (This)->lpVtbl->AddRef(This)
#define IDWriteFontFileStream_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteFontFileStream_GetFileSize(This,fileSize) (This)->lpVtbl->GetFileSize(This,fileSize)
#define IDWriteFontFileStream_ReadFileFragment(This,fragmentStart,fileOffset,fragmentSize,fragmentContext) (This)->lpVtbl->ReadFileFragment(This,fragmentStart,fileOffset,fragmentSize,fragmentContext)
#define IDWriteFontFileStream_ReleaseFileFragment(This,fragmentContext) (This)->lpVtbl->ReleaseFileFragment(This,fragmentContext)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteLocalizedStrings
DECLARE_INTERFACE_(IDWriteLocalizedStrings,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteLocalizedStrings methods */
STDMETHOD_(UINT32, GetCount)(THIS) PURE;
STDMETHOD(dummy1)(THIS);
STDMETHOD(dummy2)(THIS);
STDMETHOD(dummy3)(THIS);
STDMETHOD(dummy4)(THIS);
STDMETHOD(GetString)(THIS_
UINT32 index,
WCHAR *stringBuffer,
UINT32 size) PURE;
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteLocalizedStrings_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteLocalizedStrings_GetCount(This) (This)->lpVtbl->GetCount(This)
#define IDWriteLocalizedStrings_GetString(This,index,stringBuffer,size) (This)->lpVtbl->GetString(This,index,stringBuffer,size)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteTextFormat
DECLARE_INTERFACE_(IDWriteTextFormat,IUnknown)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IDWriteTextFormat methods */
/* rest dropped */
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteTextFormat_Release(This) (This)->lpVtbl->Release(This)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteTextLayout
DECLARE_INTERFACE_(IDWriteTextLayout,IDWriteTextFormat)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
/* IDWriteTextFormat methods */
STDMETHOD(dummy1)(THIS);
STDMETHOD(dummy2)(THIS);
STDMETHOD(dummy3)(THIS);
STDMETHOD(dummy4)(THIS);
STDMETHOD(dummy5)(THIS);
STDMETHOD(dummy6)(THIS);
STDMETHOD(dummy7)(THIS);
STDMETHOD(dummy8)(THIS);
STDMETHOD(dummy9)(THIS);
STDMETHOD(dummy10)(THIS);
STDMETHOD(dummy11)(THIS);
STDMETHOD(dummy12)(THIS);
STDMETHOD(dummy13)(THIS);
STDMETHOD(dummy14)(THIS);
STDMETHOD(dummy15)(THIS);
STDMETHOD(dummy16)(THIS);
STDMETHOD(dummy17)(THIS);
STDMETHOD(dummy18)(THIS);
STDMETHOD(dummy19)(THIS);
STDMETHOD(dummy20)(THIS);
STDMETHOD(dummy21)(THIS);
STDMETHOD(dummy22)(THIS);
STDMETHOD(dummy23)(THIS);
STDMETHOD(dummy24)(THIS);
STDMETHOD(dummy25)(THIS);
#endif
/* IDWriteTextLayout methods */
STDMETHOD(dummy26)(THIS);
STDMETHOD(dummy27)(THIS);
STDMETHOD(dummy28)(THIS);
STDMETHOD(dummy29)(THIS);
STDMETHOD(dummy30)(THIS);
STDMETHOD(dummy31)(THIS);
STDMETHOD(dummy32)(THIS);
STDMETHOD(dummy33)(THIS);
STDMETHOD(dummy34)(THIS);
STDMETHOD(dummy35)(THIS);
STDMETHOD(dummy36)(THIS);
STDMETHOD(dummy37)(THIS);
STDMETHOD(dummy38)(THIS);
STDMETHOD(dummy39)(THIS);
STDMETHOD(dummy40)(THIS);
STDMETHOD(dummy41)(THIS);
STDMETHOD(dummy42)(THIS);
STDMETHOD(dummy43)(THIS);
STDMETHOD(dummy44)(THIS);
STDMETHOD(dummy45)(THIS);
STDMETHOD(dummy46)(THIS);
STDMETHOD(dummy47)(THIS);
STDMETHOD(dummy48)(THIS);
STDMETHOD(dummy49)(THIS);
STDMETHOD(dummy50)(THIS);
STDMETHOD(dummy51)(THIS);
STDMETHOD(dummy52)(THIS);
STDMETHOD(dummy53)(THIS);
STDMETHOD(dummy54)(THIS);
STDMETHOD(dummy55)(THIS);
STDMETHOD(Draw)(THIS_
void *clientDrawingContext,
IDWriteTextRenderer *renderer,
FLOAT originX,
FLOAT originY) PURE;
/* rest dropped */
END_INTERFACE
};
#ifdef COBJMACROS
#define IDWriteTextLayout_Release(This) (This)->lpVtbl->Release(This)
#define IDWriteTextLayout_Draw(This,clientDrawingContext,renderer,originX,originY) (This)->lpVtbl->Draw(This,clientDrawingContext,renderer,originX,originY)
#endif /*COBJMACROS*/
#undef INTERFACE
#define INTERFACE IDWriteTextRenderer
DECLARE_INTERFACE_(IDWriteTextRenderer,IDWritePixelSnapping)
{
BEGIN_INTERFACE
#ifndef __cplusplus
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
/* IDWritePixelSnapping methods */
STDMETHOD(IsPixelSnappingDisabled)(THIS_
void *clientDrawingContext,
BOOL *isDisabled) PURE;
STDMETHOD(GetCurrentTransform)(THIS_
void *clientDrawingContext,
DWRITE_MATRIX *transform) PURE;
STDMETHOD(GetPixelsPerDip)(THIS_
void *clientDrawingContext,
FLOAT *pixelsPerDip) PURE;
#endif
/* IDWriteTextRenderer methods */
STDMETHOD(DrawGlyphRun)(THIS_
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
DWRITE_GLYPH_RUN const *glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription,
IUnknown* clientDrawingEffect) PURE;
STDMETHOD(DrawUnderline)(THIS_
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_UNDERLINE const *underline,
IUnknown *clientDrawingEffect) PURE;
STDMETHOD(DrawStrikethrough)(THIS_
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_STRIKETHROUGH const *strikethrough,
IUnknown* clientDrawingEffect) PURE;
STDMETHOD(DrawInlineObject)(THIS_
void *clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject *inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown *clientDrawingEffect) PURE;
END_INTERFACE
};
DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a,0xd838,0x4b5b,0xa2,0xe8,0x1a,0xdc,0x7d,0x93,0xdb,0x48);
DEFINE_GUID(IID_IDWritePixelSnapping, 0xeaf3a2da,0xecf4,0x4d24,0xb6,0x44,0xb3,0x4f,0x68,0x42,0x02,0x4b);
DEFINE_GUID(IID_IDWriteTextRenderer, 0xef8a8135,0x5cc6,0x45fe,0x88,0x25,0xc5,0xa0,0x72,0x4e,0xb8,0x19);
#endif /* __INC_DWRITE__ */

45
libass/libass.sym Normal file
View File

@ -0,0 +1,45 @@
ass_library_init
ass_library_done
ass_library_version
ass_set_fonts_dir
ass_set_extract_fonts
ass_set_style_overrides
ass_renderer_init
ass_renderer_done
ass_set_frame_size
ass_set_storage_size
ass_set_margins
ass_set_use_margins
ass_set_aspect_ratio
ass_set_font_scale
ass_set_hinting
ass_set_line_spacing
ass_get_available_font_providers
ass_set_fonts
ass_render_frame
ass_new_track
ass_free_track
ass_alloc_style
ass_alloc_event
ass_free_style
ass_free_event
ass_process_data
ass_process_codec_private
ass_process_chunk
ass_read_file
ass_read_memory
ass_read_styles
ass_add_font
ass_clear_fonts
ass_step_sub
ass_process_force_style
ass_set_message_cb
ass_fonts_update
ass_set_cache_limits
ass_flush_events
ass_set_shaper
ass_set_line_position
ass_set_pixel_aspect
ass_set_selective_style_override_enabled
ass_set_selective_style_override
ass_set_check_readorder

254
libass/x86/be_blur.asm Normal file
View File

@ -0,0 +1,254 @@
;******************************************************************************
;* be_blur.asm: SSE2 \be blur
;******************************************************************************
;* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
;*
;* This file is part of libass.
;*
;* Permission to use, copy, modify, and distribute this software for any
;* purpose with or without fee is hereby granted, provided that the above
;* copyright notice and this permission notice appear in all copies.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
;******************************************************************************
%include "x86/x86inc.asm"
SECTION_RODATA 32
low_word_zero: dd 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
SECTION .text
;------------------------------------------------------------------------------
; void be_blur_pass( uint8_t *buf, unsigned width,
; unsigned height, unsigned stride,
; uint16_t *tmp);
;------------------------------------------------------------------------------
INIT_XMM sse2
cglobal be_blur, 5,15,9
.skip_prologue:
mov r6, 2 ; int x = 2;
pxor xmm6, xmm6 ; __m128i temp3 = 0;
mov r7, r0 ; unsigned char *src=buf;
movzx r8, byte [r7 + 1] ; int old_pix = src[1];
movzx r9, byte [r7] ; int old_sum = src[0];
add r9, r8 ; old_sum += old_pix;
lea r12, [r4 + r3 * 2] ; unsigned char *col_sum_buf = tmp + stride * 2;
lea r14, [r1 - 2] ; tmpreg = (w-2);
and r14, -8 ; tmpreg &= (~7);
.first_loop:
movzx r10, byte [r7 + r6] ; int temp1 = src[x];
lea r11, [r8 + r10] ; int temp2 = old_pix + temp1;
mov r8, r10 ; old_pix = temp1;
lea r10, [r9 + r11] ; temp1 = old_sum + temp2;
mov r9, r11 ; old_sum = temp2;
mov word [r4 + r6 * 2], r10w ; col_pix_buf[x] = temp1;
inc r6 ; x++
cmp r6, r1 ; x < w
jl .first_loop
mov r6, 2 ; int x = 2;
lea r7, [r0 + r3] ; unsigned char *src=buf+stride;
movzx r8, byte [r7 + 1] ; int old_pix = src[1];
movzx r9, byte [r7] ; int old_sum = src[0];
add r9, r8 ; old_sum += old_pix
.second_loop:
movzx r10, byte [r7 + r6] ; int temp1 = src[x];
lea r11, [r8 + r10] ; int temp2 = old_pix + temp1;
mov r8, r10 ; old_pix = temp1;
lea r10, [r9 + r11] ; temp1 = old_sum + temp2;
mov r9, r11 ; old_sum = temp2;
movzx r11, word [r4 + r6 * 2] ; temp2 = col_pix_buf[x];
add r11, r10 ; temp2 += temp1;
mov word [r4 + r6 * 2], r10w ; col_pix_buf[x] = temp1;
mov word [r12 + r6 * 2], r11w ; col_sum_buf[x] = temp2;
inc r6 ; x++
cmp r6, r1 ; x < w
jl .second_loop
mov r5, 2 ; int y = 2;
.height_loop:
mov r10, r5; int tmpreg = y;
imul r10, r3; tmpreg *= stride;
lea r7, [r0 + r10] ; unsigned char *src=buf+y*stride;
sub r10, r3 ; tmpreg -= stride;
lea r13, [r0 + r10]; unsigned char *dst=buf+(y-1)*stride;
mov r6, 2 ; int x = 2;
movzx r10, byte [r7] ; temp1 = src[0];
movzx r11, byte [r7 + 1] ; temp2 = src[1];
add r10, r11; temp1 += temp2
movd xm0, r10d; __m128i old_pix_128 = temp2;
movd xm1, r11d; __m128i old_sum_128 = temp1;
.width_loop:
movq xmm2, [r7 + r6]; __m128i new_pix = (src+x);
punpcklbw xmm2, xmm6 ; new_pix = _mm_unpacklo_epi8(new_pix, temp3);
movdqa xmm3, xmm2 ; __m128i temp = new_pix;
pslldq xmm3, 2 ; temp = temp << 2 * 8;
paddw xmm3, xmm0 ; temp = _mm_add_epi16(temp, old_pix_128);
paddw xmm3, xmm2 ; temp = _mm_add_epi16(temp, new_pix);
movdqa xmm0, xmm2 ; old_pix_128 = new_pix;
psrldq xmm0, 14 ; old_pix_128 = old_pix_128 >> 14 * 8;
movdqa xmm2, xmm3 ; new_pix = temp;
pslldq xmm2, 2 ; new_pix = new_pix << 2 * 8;
paddw xmm2, xmm1 ; new_pix = _mm_add_epi16(new_pix, old_sum_128);
paddw xmm2, xmm3 ; new_pix = _mm_add_epi16(new_pix, temp);
movdqa xmm1, xmm3 ; old_sum_128 = temp;
psrldq xmm1, 14 ; old_sum_128 = old_sum_128 >> 14 * 8;
movdqu xmm4, [r4 + r6 * 2] ; __m128i old_col_pix = *(col_pix_buf+x);
movdqu [r4 + r6 * 2], xmm2 ; *(col_pix_buf+x) = new_pix ;
movdqu xmm5, [r12 + r6 * 2] ; __m128i old_col_sum = *(col_pix_sum+x);
movdqa xmm3, xmm2 ; temp = new_pix;
paddw xmm3, xmm4 ; temp = _mm_add_epi16(temp, old_col_pix);
movdqu [r12 + r6 * 2], xmm3 ; *(col_sum_buf+x) = temp;
paddw xmm5, xmm3 ; old_col_sum = _mm_add_epi16(old_col_sum, temp);
psrlw xmm5, 4 ; old_col_sum = old_col_sum >> 4;
packuswb xmm5, xmm5 ; old_col_sum = _mm_packus_epi16(old_col_sum, old_col_sum);
movq qword [r13 + r6 - 1], xmm5 ; *(dst+x-1) = old_col_sum;
add r6, 8; x += 8;
cmp r6, r14; x < ((w - 2) & (~7));
jl .width_loop
movzx r8, byte [r7 + r6 - 1] ; old_pix = src[x-1];
movzx r9, byte [r7 + r6 - 2] ; old_sum = old_pix + src[x-2];
add r9, r8
jmp .final_width_check
.final_width_loop:
movzx r10, byte [r7 + r6] ; temp1 = src[x];
lea r11, [r8 + r10] ; temp2 = old_pix + temp1;
mov r8, r10 ; old_pix = temp1;
lea r10, [r9 + r11] ; temp1 = old_sum + temp2;
mov r9, r11 ; old_sum = temp2;
movzx r11, word [r4 + r6 * 2] ; temp2 = col_pix_buf[x];
add r11, r10 ; temp2 += temp1;
mov word [r4 + r6 * 2], r10w ; col_pix_buf[x] = temp1;
movzx r10, word [r12 + r6 * 2] ; temp1 = col_sum_buf[x];
add r10, r11 ; temp1 += temp2;
shr r10, 4 ; temp1 >>= 4;
mov byte [r13 + r6 - 1], r10b ; dst[x-1] = temp1
mov [r12 + r6 * 2], r11w ; col_sum_buf[x] = temp2;
inc r6 ; x++
.final_width_check:
cmp r6, r1 ; x < w
jl .final_width_loop
inc r5 ; y++;
cmp r5, r2 ; y < h;
jl .height_loop
RET
INIT_YMM avx2
cglobal be_blur, 5,15,9
cmp r1, 32
jl be_blur_sse2.skip_prologue
mov r6, 2 ; int x = 2;
vpxor ymm6, ymm6 ; __m128i temp3 = 0;
mov r7, r0 ; unsigned char *src=buf;
movzx r8, byte [r7 + 1] ; int old_pix = src[1];
movzx r9, byte [r7] ; int old_sum = src[0];
add r9, r8 ; old_sum += old_pix;
lea r12, [r4 + r3 * 2] ; unsigned char *col_sum_buf = tmp + stride * 2;
lea r14, [r1 - 2] ; tmpreg = (w-2);
and r14, -16 ; tmpreg &= (~15);
vmovdqa ymm7, [low_word_zero]
.first_loop:
movzx r10, byte [r7 + r6] ; int temp1 = src[x];
lea r11, [r8 + r10] ; int temp2 = old_pix + temp1;
mov r8, r10 ; old_pix = temp1;
lea r10, [r9 + r11] ; temp1 = old_sum + temp2;
mov r9, r11 ; old_sum = temp2;
mov word [r4 + r6 * 2], r10w ; col_pix_buf[x] = temp1;
inc r6 ; x++
cmp r6, r1 ; x < w
jl .first_loop
mov r6, 2 ; int x = 2;
lea r7, [r0 + r3] ; unsigned char *src=buf+stride;
movzx r8, byte [r7 + 1] ; int old_pix = src[1];
movzx r9, byte [r7] ; int old_sum = src[0];
add r9, r8 ; old_sum += old_pix
.second_loop:
movzx r10, byte [r7 + r6] ; int temp1 = src[x];
lea r11, [r8 + r10] ; int temp2 = old_pix + temp1;
mov r8, r10 ; old_pix = temp1;
lea r10, [r9 + r11] ; temp1 = old_sum + temp2;
mov r9, r11 ; old_sum = temp2;
movzx r11, word [r4 + r6 * 2] ; temp2 = col_pix_buf[x];
add r11, r10 ; temp2 += temp1;
mov word [r4 + r6 * 2], r10w ; col_pix_buf[x] = temp1;
mov word [r12 + r6 * 2], r11w ; col_sum_buf[x] = temp2;
inc r6 ; x++
cmp r6, r1 ; x < w
jl .second_loop
mov r5, 2 ; int y = 2;
.height_loop:
mov r10, r5; int tmpreg = y;
imul r10, r3; tmpreg *= stride;
lea r7, [r0 + r10] ; unsigned char *src=buf+y*stride;
sub r10, r3 ; tmpreg -= stride;
lea r13, [r0 + r10]; unsigned char *dst=buf+(y-1)*stride;
mov r6, 2 ; int x = 2;
movzx r10, byte [r7] ; temp1 = src[0];
movzx r11, byte [r7 + 1] ; temp2 = src[1];
add r10, r11; temp1 += temp2
vmovd xmm0, r10d; __m128i old_pix_128 = temp2;
vmovd xmm1, r11d; __m128i old_sum_128 = temp1;
.width_loop:
vpermq ymm2, [r7 + r6], 0x10
vpunpcklbw ymm2, ymm2, ymm6 ; new_pix = _mm_unpacklo_epi8(new_pix, temp3);
vpermq ymm8, ymm2, 0x4e
vpalignr ymm3, ymm2, ymm8, 14
vpand ymm3, ymm3, ymm7
vpaddw ymm3, ymm0 ; temp = _mm_add_epi16(temp, old_pix_128);
vpaddw ymm3, ymm2 ; temp = _mm_add_epi16(temp, new_pix);
vperm2i128 ymm0, ymm2, ymm6, 0x21
vpsrldq ymm0, ymm0, 14; temp = temp >> 14 * 8;
vpermq ymm8, ymm3, 0x4e
vpand ymm8, ymm8, ymm7;
vpalignr ymm2, ymm3, ymm8, 14
vpand ymm2, ymm2, ymm7
vpaddw ymm2, ymm1 ; new_pix = _mm_add_epi16(new_pix, old_sum_128);
vpaddw ymm2, ymm3 ; new_pix = _mm_add_epi16(new_pix, temp);
vperm2i128 ymm1, ymm3, ymm6, 0x21
vpsrldq ymm1, ymm1, 14; temp = temp << 2 * 8;
vmovdqu ymm4, [r4 + r6 * 2] ; __m128i old_col_pix = *(col_pix_buf+x);
vmovdqu [r4 + r6 * 2], ymm2 ; *(col_pix_buf+x) = new_pix ;
vmovdqu ymm5, [r12 + r6 * 2] ; __m128i old_col_sum = *(col_pix_sum+x);
vpaddw ymm3, ymm2, ymm4
vmovdqu [r12 + r6 * 2], ymm3 ; *(col_sum_buf+x) = temp;
vpaddw ymm5, ymm3 ; old_col_sum = _mm_add_epi16(old_col_sum, temp);
vpsrlw ymm5, 4 ; old_col_sum = old_col_sum >> 4;
vpackuswb ymm5, ymm5 ; old_col_sum = _mm_packus_epi16(old_col_sum, old_col_sum);
vpermq ymm5, ymm5, 11_01_10_00b
vmovdqu [r13 + r6 - 1], xmm5 ; *(dst+x-1) = old_col_sum;
add r6, 16; x += 16;
cmp r6, r14; x < ((w - 2) & (~15));
jl .width_loop
movzx r8, byte [r7 + r6 - 1] ; old_pix = src[x-1];
movzx r9, byte [r7 + r6 - 2] ; old_sum = old_pix + src[x-2];
add r9, r8
jmp .final_width_check
.final_width_loop:
movzx r10, byte [r7 + r6] ; temp1 = src[x];
lea r11, [r8 + r10] ; temp2 = old_pix + temp1;
mov r8, r10 ; old_pix = temp1;
lea r10, [r9 + r11] ; temp1 = old_sum + temp2;
mov r9, r11 ; old_sum = temp2;
movzx r11, word [r4 + r6 * 2] ; temp2 = col_pix_buf[x];
add r11, r10 ; temp2 += temp1;
mov word [r4 + r6 * 2], r10w ; col_pix_buf[x] = temp1;
movzx r10, word [r12 + r6 * 2] ; temp1 = col_sum_buf[x];
add r10, r11 ; temp1 += temp2;
shr r10, 4 ; temp1 >>= 4;
mov byte [r13 + r6 - 1], r10b ; dst[x-1] = temp1
mov [r12 + r6 * 2], r11w ; col_sum_buf[x] = temp2;
inc r6 ; x++
.final_width_check:
cmp r6, r1 ; x < w
jl .final_width_loop
inc r5 ; y++;
cmp r5, r2 ; y < h;
jl .height_loop
RET

View File

@ -0,0 +1,305 @@
;******************************************************************************
;* add_bitmaps.asm: SSE2 and x86 add_bitmaps
;******************************************************************************
;* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
;*
;* This file is part of libass.
;*
;* Permission to use, copy, modify, and distribute this software for any
;* purpose with or without fee is hereby granted, provided that the above
;* copyright notice and this permission notice appear in all copies.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
;******************************************************************************
%include "x86/x86inc.asm"
SECTION_RODATA 32
words_255: dw 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
SECTION .text
;------------------------------------------------------------------------------
; void add_bitmaps( uint8_t *dst, intptr_t dst_stride,
; uint8_t *src, intptr_t src_stride,
; intptr_t height, intptr_t width );
;------------------------------------------------------------------------------
INIT_XMM
cglobal add_bitmaps_x86, 6,7
.skip_prologue:
imul r4, r3
add r4, r2
PUSH r4
mov r4, r3
.height_loop:
xor r6, r6 ; x offset
.stride_loop:
movzx r3, byte [r0 + r6]
add r3b, byte [r2 + r6]
jnc .continue
mov r3b, 0xff
.continue:
mov byte [r0 + r6], r3b
inc r6
cmp r6, r5
jl .stride_loop ; still in scan line
add r0, r1
add r2, r4
cmp r2, [rsp]
jl .height_loop
ADD rsp, gprsize
RET
%macro ADD_BITMAPS 0
cglobal add_bitmaps, 6,7
.skip_prologue:
cmp r5, mmsize
%if mmsize == 16
jl add_bitmaps_x86.skip_prologue
%else
jl add_bitmaps_sse2.skip_prologue
%endif
%if mmsize == 32
vzeroupper
%endif
imul r4, r3
add r4, r2 ; last address
.height_loop:
xor r6, r6 ; x offset
.stride_loop:
movu m0, [r0 + r6]
paddusb m0, [r2 + r6]
movu [r0 + r6], m0
add r6, mmsize
cmp r6, r5
jl .stride_loop ; still in scan line
add r0, r1
add r2, r3
cmp r2, r4
jl .height_loop
RET
%endmacro
INIT_XMM sse2
ADD_BITMAPS
INIT_YMM avx2
ADD_BITMAPS
;------------------------------------------------------------------------------
; void sub_bitmaps( uint8_t *dst, intptr_t dst_stride,
; uint8_t *src, intptr_t src_stride,
; intptr_t height, intptr_t width );
;------------------------------------------------------------------------------
INIT_XMM
cglobal sub_bitmaps_x86, 6,10
.skip_prologue:
imul r4, r3
add r4, r2 ; last address
PUSH r4
mov r4, r3
.height_loop:
xor r6, r6 ; x offset
.stride_loop:
mov r3b, byte [r0 + r6]
sub r3b, byte [r2 + r6]
jnc .continue
mov r3b, 0x0
.continue:
mov byte [r0 + r6], r3b
inc r6
cmp r6, r5
jl .stride_loop ; still in scan line
add r0, r1
add r2, r4
cmp r2, [rsp]
jl .height_loop
ADD rsp, gprsize
RET
%if ARCH_X86_64
%macro SUB_BITMAPS 0
cglobal sub_bitmaps, 6,10
.skip_prologue:
cmp r5, mmsize
%if mmsize == 16
jl sub_bitmaps_x86.skip_prologue
%else
jl sub_bitmaps_sse2.skip_prologue
%endif
%if mmsize == 32
vzeroupper
%endif
imul r4, r3
add r4, r2 ; last address
mov r7, r5
and r7, -mmsize ; &= (16);
xor r9, r9
.height_loop:
xor r6, r6 ; x offset
.stride_loop:
movu m0, [r0 + r6]
movu m1, [r2 + r6]
psubusb m0, m1
movu [r0 + r6], m0
add r6, mmsize
cmp r6, r7
jl .stride_loop ; still in scan line
.stride_loop2:
cmp r6, r5
jge .finish
movzx r8, byte [r0 + r6]
sub r8b, byte [r2 + r6]
cmovc r8, r9
mov byte [r0 + r6], r8b
inc r6
jmp .stride_loop2
.finish:
add r0, r1
add r2, r3
cmp r2, r4
jl .height_loop
RET
%endmacro
INIT_XMM sse2
SUB_BITMAPS
INIT_YMM avx2
SUB_BITMAPS
;------------------------------------------------------------------------------
; void mul_bitmaps( uint8_t *dst, intptr_t dst_stride,
; uint8_t *src1, intptr_t src1_stride,
; uint8_t *src2, intptr_t src2_stride,
; intptr_t width, intptr_t height );
;------------------------------------------------------------------------------
INIT_XMM
cglobal mul_bitmaps_x86, 8,12
.skip_prologue:
imul r7, r3
add r7, r2 ; last address
.height_loop:
xor r8, r8 ; x offset
.stride_loop:
movzx r9, byte [r2 + r8]
movzx r10, byte [r4 + r8]
imul r9, r10
add r9, 255
shr r9, 8
mov byte [r0 + r8], r9b
inc r8
cmp r8, r6
jl .stride_loop ; still in scan line
add r0, r1
add r2, r3
add r4, r5
cmp r2, r7
jl .height_loop
RET
INIT_XMM sse2
cglobal mul_bitmaps, 8,12
.skip_prologue:
cmp r6, 8
jl mul_bitmaps_x86.skip_prologue
imul r7, r3
add r7, r2 ; last address
pxor xmm2, xmm2
movdqa xmm3, [words_255]
mov r9, r6
and r9, -8 ; &= (~8);
.height_loop:
xor r8, r8 ; x offset
.stride_loop:
movq xmm0, [r2 + r8]
movq xmm1, [r4 + r8]
punpcklbw xmm0, xmm2
punpcklbw xmm1, xmm2
pmullw xmm0, xmm1
paddw xmm0, xmm3
psrlw xmm0, 0x08
packuswb xmm0, xmm0
movq [r0 + r8], xmm0
add r8, 8
cmp r8, r9
jl .stride_loop ; still in scan line
.stride_loop2:
cmp r8, r6
jge .finish
movzx r10, byte [r2 + r8]
movzx r11, byte [r4 + r8]
imul r10, r11
add r10, 255
shr r10, 8
mov byte [r0 + r8], r10b
inc r8
jmp .stride_loop2
.finish:
add r0, r1
add r2, r3
add r4, r5
cmp r2, r7
jl .height_loop
RET
INIT_YMM avx2
cglobal mul_bitmaps, 8,12
cmp r6, 16
jl mul_bitmaps_sse2.skip_prologue
%if mmsize == 32
vzeroupper
%endif
imul r7, r3
add r7, r2 ; last address
vpxor ymm2, ymm2
vmovdqa ymm3, [words_255]
mov r9, r6
and r9, -16 ; &= (~16);
.height_loop:
xor r8, r8 ; x offset
.stride_loop:
vmovdqu xmm0, [r2 + r8]
vpermq ymm0, ymm0, 0x10
vmovdqu xmm1, [r4 + r8]
vpermq ymm1, ymm1, 0x10
vpunpcklbw ymm0, ymm0, ymm2
vpunpcklbw ymm1, ymm1, ymm2
vpmullw ymm0, ymm0, ymm1
vpaddw ymm0, ymm0, ymm3
vpsrlw ymm0, ymm0, 0x08
vextracti128 xmm4, ymm0, 0x1
vpackuswb ymm0, ymm0, ymm4
vmovdqa [r0 + r8], xmm0
add r8, 16
cmp r8, r9
jl .stride_loop ; still in scan line
.stride_loop2:
cmp r8, r6
jge .finish
movzx r10, byte [r2 + r8]
movzx r11, byte [r4 + r8]
imul r10, r11
add r10, 255
shr r10, 8
mov byte [r0 + r8], r10b
inc r8
jmp .stride_loop2
.finish:
add r0, r1
add r2, r3
add r4, r5
cmp r2, r7
jl .height_loop
RET
%endif

1423
libass/x86/blur.asm Normal file

File diff suppressed because it is too large Load Diff

64
libass/x86/cpuid.asm Normal file
View File

@ -0,0 +1,64 @@
;******************************************************************************
;* add_bitmaps.asm: SSE2 and x86 add_bitmaps
;******************************************************************************
;* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
;*
;* This file is part of libass.
;*
;* Permission to use, copy, modify, and distribute this software for any
;* purpose with or without fee is hereby granted, provided that the above
;* copyright notice and this permission notice appear in all copies.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
;******************************************************************************
%include "x86/x86inc.asm"
SECTION .text
;------------------------------------------------------------------------------
; void get_cpuid( uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
;------------------------------------------------------------------------------
INIT_XMM
cglobal get_cpuid, 4, 5, 0
push rbx
push r3
push r2
push r1
push r0
mov eax, [r0]
xor ecx, ecx
cpuid
pop r4
mov [r4], eax
pop r4
mov [r4], ebx
pop r4
mov [r4], ecx
pop r4
mov [r4], edx
pop rbx
RET
;-----------------------------------------------------------------------------
; void get_xgetbv( uint32_t op, uint32_t *eax, uint32_t *edx )
;-----------------------------------------------------------------------------
INIT_XMM
cglobal get_xgetbv, 3, 7, 0
push r2
push r1
mov ecx, r0d
xgetbv
pop r4
mov [r4], eax
pop r4
mov [r4], edx
RET

25
libass/x86/cpuid.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef INTEL_CPUID_H
#define INTEL_CPUID_H
void ass_get_cpuid( uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
void ass_get_xgetbv( uint32_t op, uint32_t *eax, uint32_t *edx );
#endif

858
libass/x86/rasterizer.asm Normal file
View File

@ -0,0 +1,858 @@
;******************************************************************************
;* rasterizer.asm: SSE2/AVX2 tile rasterization
;******************************************************************************
;* Copyright (C) 2014 Vabishchevich Nikolay <vabnick@gmail.com>
;*
;* This file is part of libass.
;*
;* Permission to use, copy, modify, and distribute this software for any
;* purpose with or without fee is hereby granted, provided that the above
;* copyright notice and this permission notice appear in all copies.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
;******************************************************************************
%include "x86/utils.asm"
SECTION_RODATA 32
words_index: dw 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
words_tile16: times 16 dw 1024
words_tile32: times 16 dw 512
SECTION .text
;------------------------------------------------------------------------------
; FILL_LINE 1:dst, 2:m_src, 3:size
;------------------------------------------------------------------------------
%macro FILL_LINE 3
%if ((%3) & (mmsize - 1)) == 0
%assign %%i 0
%rep (%3) / mmsize
mova [%1 + %%i], m%2
%assign %%i %%i + mmsize
%endrep
%elif (%3) == 16
mova [%1], xm%2
%else
%error "invalid line size"
%endif
%endmacro
;------------------------------------------------------------------------------
; FILL_SOLID_TILE 1:tile_order, 2:suffix
; void fill_solid_tile%2(uint8_t *buf, ptrdiff_t stride, int set);
;------------------------------------------------------------------------------
%macro FILL_SOLID_TILE 2
cglobal fill_solid_tile%2, 3,4,1
mov r3d, -1
test r2d, r2d
cmovnz r2d, r3d
movd xm0, r2d
%if mmsize == 32
vpbroadcastd m0, xm0
%else
pshufd m0, m0, q0000
%endif
%rep (1 << %1) - 1
FILL_LINE r0, 0, 1 << %1
add r0, r1
%endrep
FILL_LINE r0, 0, 1 << %1
RET
%endmacro
INIT_XMM sse2
FILL_SOLID_TILE 4,16
FILL_SOLID_TILE 5,32
INIT_YMM avx2
FILL_SOLID_TILE 4,16
FILL_SOLID_TILE 5,32
;------------------------------------------------------------------------------
; CALC_LINE 1:tile_order, 2:m_dst, 3:m_src, 4:m_delta,
; 5:m_zero, 6:m_full, 7:m_tmp
; Calculate line using antialiased halfplane algorithm
;------------------------------------------------------------------------------
%macro CALC_LINE 7
paddw m%7, m%3, m%4
pmaxsw m%2, m%3, m%5
pmaxsw m%7, m%5
pminsw m%2, m%6
pminsw m%7, m%6
paddw m%2, m%7
psraw m%2, 7 - %1
%endmacro
;------------------------------------------------------------------------------
; DEF_A_SHIFT 1:tile_order
; If single mm-register is enough to store the whole line
; then sets a_shift = 0,
; else sets a_shift = log2(mmsize / sizeof(int16_t)).
;------------------------------------------------------------------------------
%macro DEF_A_SHIFT 1
%if mmsize >= (2 << %1)
%define a_shift 0
%elif mmsize == 32
%define a_shift 4
%elif mmsize == 16
%define a_shift 3
%else
%error "invalid mmsize"
%endif
%endmacro
;------------------------------------------------------------------------------
; FILL_HALFPLANE_TILE 1:tile_order, 2:suffix
; void fill_halfplane_tile%2(uint8_t *buf, ptrdiff_t stride,
; int32_t a, int32_t b, int64_t c, int32_t scale);
;------------------------------------------------------------------------------
%macro FILL_HALFPLANE_TILE 2
DEF_A_SHIFT %1
%if ARCH_X86_64 && a_shift
cglobal fill_halfplane_tile%2, 6,7,9
%else
cglobal fill_halfplane_tile%2, 6,7,8
%endif
%if a_shift == 0
SWAP 3, 8
%endif
%if ARCH_X86_64
movsxd r2, r2d ; a
movsxd r3, r3d ; b
sar r4, 7 + %1 ; c >> (tile_order + 7)
movsxd r5, r5d ; scale
mov r6, 1 << (45 + %1)
imul r2, r5
add r2, r6
sar r2, 46 + %1 ; aa
imul r3, r5
add r3, r6
sar r3, 46 + %1 ; bb
imul r4, r5
shr r6, 1 + %1
add r4, r6
sar r4, 45 ; cc
%else
mov r0d, r4m ; c_lo
mov r2d, r5m ; c_hi
mov r1d, r6m ; scale
mov r5d, 1 << 12
shr r0d, 7 + %1
shl r2d, 25 - %1
or r0d, r2d ; r0d (eax) = c >> (tile_order + 7)
imul r1d ; r2d (edx) = (c >> ...) * scale >> 32
add r2d, r5d
sar r2d, 13
mov r4d, r2d ; cc
shl r5d, 1 + %1
mov r0d, r3m ; r0d (eax) = b
imul r1d ; r2d (edx) = b * scale >> 32
add r2d, r5d
sar r2d, 14 + %1
mov r3d, r2d ; bb
mov r0d, r2m ; r0d (eax) = a
imul r1d ; r2d (edx) = a * scale >> 32
add r2d, r5d
sar r2d, 14 + %1 ; aa
mov r0d, r0m
mov r1d, r1m
%endif
add r4d, 1 << (13 - %1)
mov r6d, r2d
add r6d, r3d
sar r6d, 1
sub r4d, r6d
BCASTW 1, r4d ; cc
BCASTW 2, r2d ; aa
%if a_shift
psllw m3, m2, a_shift ; aa * (mmsize / 2)
%endif
pmullw m2, [words_index]
psubw m1, m2 ; cc - aa * i
mov r4d, r2d ; aa
mov r6d, r4d
sar r6d, 31
xor r4d, r6d
sub r4d, r6d ; abs_a
mov r5d, r3d ; bb
mov r6d, r5d
sar r6d, 31
xor r5d, r6d
sub r5d, r6d ; abs_b
cmp r4d, r5d
cmovg r4d, r5d
add r4d, 2
sar r4d, 2 ; delta
BCASTW 2, r4d
psubw m1, m2 ; c1 = cc - aa * i - delta
paddw m2, m2 ; 2 * delta
%if a_shift
MUL r2d, (1 << %1) - (mmsize / 2)
sub r3d, r2d ; bb - (tile_size - mmsize / 2) * aa
%endif
%if ARCH_X86_64 || a_shift == 0
BCASTW 8, r3d
%endif
pxor m0, m0
mova m4, [words_tile%2]
mov r2d, (1 << %1)
jmp .loop_entry
.loop_start:
add r0, r1
%if ARCH_X86_64 || a_shift == 0
psubw m1, m8
%else
BCASTW 7, r3d
psubw m1, m7
%endif
.loop_entry:
%assign i 0
%rep (1 << %1) / mmsize
%if i
psubw m1, m3
%endif
CALC_LINE %1, 5, 1,2, 0,4, 7
psubw m1, m3
CALC_LINE %1, 6, 1,2, 0,4, 7
packuswb m5, m6
%if mmsize == 32
vpermq m5, m5, q3120
%endif
mova [r0 + i], m5
%assign i i + mmsize
%endrep
%if (1 << %1) < mmsize
CALC_LINE %1, 5, 1,2, 0,4, 7
packuswb m5, m6
vpermq m5, m5, q3120
mova [r0 + i], xm5
%endif
sub r2d,1
jnz .loop_start
RET
%endmacro
INIT_XMM sse2
FILL_HALFPLANE_TILE 4,16
FILL_HALFPLANE_TILE 5,32
INIT_YMM avx2
FILL_HALFPLANE_TILE 4,16
FILL_HALFPLANE_TILE 5,32
;------------------------------------------------------------------------------
; struct segment {
; int64_t c;
; int32_t a, b, scale, flags;
; int32_t x_min, x_max, y_min, y_max;
; };
;------------------------------------------------------------------------------
struc line
.c: resq 1
.a: resd 1
.b: resd 1
.scale: resd 1
.flags: resd 1
.x_min: resd 1
.x_max: resd 1
.y_min: resd 1
.y_max: resd 1
endstruc
;------------------------------------------------------------------------------
; ZEROFILL 1:dst, 2:size, 3:tmp
;------------------------------------------------------------------------------
%macro ZEROFILL 3
%assign %%n 128 / mmsize
mov %3, (%2) / 128
%%zerofill_loop:
%assign %%i 0
%rep %%n
mova [%1 + %%i], mm_zero
%assign %%i %%i + mmsize
%endrep
add %1, 128
sub %3, 1
jnz %%zerofill_loop
%assign %%i 0
%rep ((%2) / mmsize) & (%%n - 1)
mova [%1 + %%i], mm_zero
%assign %%i %%i + mmsize
%endrep
%endmacro
;------------------------------------------------------------------------------
; CALC_DELTA_FLAG 1:res, 2:line, 3-4:tmp
; Set bits of result register (res):
; bit 3 - for nonzero up_delta,
; bit 2 - for nonzero dn_delta.
;------------------------------------------------------------------------------
%macro CALC_DELTA_FLAG 4
mov %3d, [%2 + line.flags]
xor %4d, %4d
cmp %4d, [%2 + line.x_min]
cmovz %4d, %3d
xor %1d, %1d
test %3d, 2 ; SEGFLAG_UL_DR
cmovnz %1d, %4d
shl %3d, 2
xor %1d, %3d
and %4d, 4
and %1d, 4
lea %1d, [%1d + 2 * %1d]
xor %1d, %4d
%endmacro
;------------------------------------------------------------------------------
; UPDATE_DELTA 1:dn/up, 2:dst, 3:flag, 4:pos, 5:tmp
; Update delta array
;------------------------------------------------------------------------------
%macro UPDATE_DELTA 5
%ifidn %1, dn
%define %%op add
%define %%opi sub
%assign %%flag 1 << 2
%elifidn %1, up
%define %%op sub
%define %%opi add
%assign %%flag 1 << 3
%else
%error "dn/up expected"
%endif
test %3d, %%flag
jz %%skip
lea %5d, [4 * %4d - 256]
%%opi [%2], %5w
lea %5d, [4 * %4d]
%%op [%2 + 2], %5w
%%skip:
%endmacro
;------------------------------------------------------------------------------
; CALC_VBA 1:tile_order, 2:b
; Calculate b - (tile_size - (mmsize / sizeof(int16_t))) * a
;------------------------------------------------------------------------------
%macro CALC_VBA 2
BCASTW m_vba, %2d
%rep (2 << %1) / mmsize - 1
psubw mm_vba, mm_van
%endrep
%endmacro
;------------------------------------------------------------------------------
; FILL_BORDER_LINE 1:tile_order, 2:res, 3:abs_a[abs_ab], 4:b, 5:[abs_b],
; 6:size, 7:sum, 8-9:tmp, 10-14:m_tmp, 15:[m_tmp]
; Render top/bottom line of the trapezium with antialiasing
;------------------------------------------------------------------------------
%macro FILL_BORDER_LINE 15
mov %8d, %6d
shl %8d, 8 - %1 ; size << (8 - tile_order)
xor %9d, %9d
%if ARCH_X86_64
sub %8d, %3d ; abs_a
cmovg %8d, %9d
add %8d, 1 << (14 - %1)
shl %8d, 2 * %1 - 5 ; w
BCASTW %15, %8d
mov %9d, %5d ; abs_b
imul %9d, %6d
sar %9d, 6 ; dc_b
cmp %9d, %3d ; abs_a
cmovg %9d, %3d
%else
sub %8w, %3w ; abs_a
cmovg %8d, %9d
add %8w, 1 << (14 - %1)
shl %8d, 2 * %1 - 5 ; w
mov %9d, %3d ; abs_ab
shr %9d, 16 ; abs_b
imul %9d, %6d
sar %9d, 6 ; dc_b
cmp %9w, %3w
cmovg %9w, %3w
%endif
add %9d, 2
sar %9d, 2 ; dc
imul %7d, %4d ; sum * b
sar %7d, 7 ; avg * b
add %7d, %9d ; avg * b + dc
add %9d, %9d ; 2 * dc
imul %7d, %8d
sar %7d, 16
sub %7d, %6d ; -offs1
BCASTW %10, %7d
imul %9d, %8d
sar %9d, 16 ; offs2 - offs1
BCASTW %11, %9d
add %6d, %6d
BCASTW %12, %6d
%assign %%i 0
%rep (2 << %1) / mmsize
%if %%i
psubw mm_c, mm_van
%endif
%if ARCH_X86_64
pmulhw m%13, mm_c, m%15
%else
BCASTW %14, %8d
pmulhw m%13, mm_c, m%14
%endif
psubw m%13, m%10 ; c1
paddw m%14, m%13, m%11 ; c2
pmaxsw m%13, mm_zero
pmaxsw m%14, mm_zero
pminsw m%13, m%12
pminsw m%14, m%12
paddw m%13, m%14
paddw m%13, [%2 + %%i]
mova [%2 + %%i], m%13
%assign %%i %%i + mmsize
%endrep
%endmacro
;------------------------------------------------------------------------------
; SAVE_RESULT 1:tile_order, 2:buf, 3:stride, 4:src, 5:delta,
; 6-7:tmp, 8-11:m_tmp
; Convert and store internal buffer (with delta array) in the result buffer
;------------------------------------------------------------------------------
%macro SAVE_RESULT 11
mov %6d, 1 << %1
xor %7d, %7d
%%save_loop:
add %7w, [%5]
BCASTW %10, %7d
add %5, 2
%assign %%i 0
%rep (1 << %1) / mmsize
paddw m%8, m%10, [%4 + 2 * %%i]
PABSW %8, %11
paddw m%9, m%10, [%4 + 2 * %%i + mmsize]
PABSW %9, %11
packuswb m%8, m%9
%if mmsize == 32
vpermq m%8, m%8, q3120
%endif
mova [%2 + %%i], m%8
%assign %%i %%i + mmsize
%endrep
%if (1 << %1) < mmsize
paddw m%8, m%10, [%4 + 2 * %%i]
PABSW %8, %11
packuswb m%8, m%8
vpermq m%8, m%8, q3120
mova [%2 + %%i], xm%8
%endif
add %2, %3
add %4, 2 << %1
sub %6d, 1
jnz %%save_loop
%endmacro
;------------------------------------------------------------------------------
; GET_RES_ADDR 1:dst
; CALC_RES_ADDR 1:tile_order, 2:dst/index, 3:tmp, 4:[skip_calc]
; Calculate position of line in the internal buffer
;------------------------------------------------------------------------------
%macro GET_RES_ADDR 1
%if mmsize <= 16 && HAVE_ALIGNED_STACK
mov %1, rstk
%else
lea %1, [rstk + mmsize - 1]
and %1, ~(mmsize - 1)
%endif
%endmacro
%macro CALC_RES_ADDR 3-4 noskip
shl %2d, 1 + %1
%if mmsize <= 16 && HAVE_ALIGNED_STACK
add %2, rstk
%else
%ifidn %4, noskip
lea %3, [rstk + mmsize - 1]
and %3, ~(mmsize - 1)
%endif
add %2, %3
%endif
%endmacro
;------------------------------------------------------------------------------
; FILL_GENERIC_TILE 1:tile_order, 2:suffix
; void fill_generic_tile%2(uint8_t *buf, ptrdiff_t stride,
; const struct segment *line, size_t n_lines,
; int winding);
;------------------------------------------------------------------------------
%macro FILL_GENERIC_TILE 2
; t3=line t4=up/cur t5=dn/end t6=dn_pos t7=up_pos
; t8=a/abs_a/abs_ab t9=b t10=c/abs_b
%if ARCH_X86_64
DECLARE_REG_TMP 10,11,5,2, 4,9,6,7, 8,12,13
%else
DECLARE_REG_TMP 0,1,5,3, 4,6,6,0, 2,3,5
%endif
%assign tile_size 1 << %1
%assign delta_offs 2 * tile_size * tile_size
%assign alloc_size 2 * tile_size * (tile_size + 1) + 4
%assign buf_size 2 * tile_size * (tile_size + 1)
DEF_A_SHIFT %1
%if ARCH_X86_64
%define m_zero 6
%define m_full 7
%define mm_index m8
%define m_c 9
%define m_vba 10
%if a_shift
%define m_van 11
cglobal fill_generic_tile%2, 5,14,12
%else
cglobal fill_generic_tile%2, 5,14,11
%endif
%else
%define m_zero 5
%define m_full 4 ; tmp
%define mm_index [words_index]
%define m_c 7
%if a_shift
%define m_van 6
%define m_vba 3 ; tmp
%else
%define m_vba 6
%endif
%assign alloc_size alloc_size + 8
cglobal fill_generic_tile%2, 0,7,8
%endif
%define mm_zero m %+ m_zero
%define mm_full m %+ m_full
%define mm_c m %+ m_c
%define mm_vba m %+ m_vba
%if a_shift
%define mm_van m %+ m_van
%endif
%if mmsize <= 16 && HAVE_ALIGNED_STACK
%assign alloc_size alloc_size + stack_offset + gprsize + (mmsize - 1)
%assign alloc_size (alloc_size & ~(mmsize - 1)) - stack_offset - gprsize
%else
%assign alloc_size alloc_size + 2 * mmsize
%assign delta_offs delta_offs + mmsize
%assign buf_size buf_size + mmsize
%endif
SUB rstk, alloc_size
GET_RES_ADDR t0
pxor mm_zero, mm_zero
ZEROFILL t0, buf_size, t1
%if ARCH_X86_64 == 0
mov r4d, r4m
%endif
shl r4d, 8
mov [rstk + delta_offs], r4w
%if ARCH_X86_64
mova mm_index, [words_index]
mova mm_full, [words_tile%2]
%define dn_addr t5
%else
%define dn_addr [rstk + delta_offs + 2 * tile_size + 4]
%define dn_pos [rstk + delta_offs + 2 * tile_size + 8]
%endif
.line_loop:
%if ARCH_X86_64 == 0
mov t3, r2m
lea t0, [t3 + line_size]
mov r2m, t0
%endif
CALC_DELTA_FLAG t0, t3, t1,t2
mov t4d, [t3 + line.y_min]
mov t2d, [t3 + line.y_max]
%if ARCH_X86_64
mov t8d, t4d
mov t6d, t4d
and t6d, 63 ; up_pos
shr t4d, 6 ; up
mov t5d, t2d
mov t7d, t2d
and t7d, 63 ; dn_pos
shr t5d, 6 ; dn
UPDATE_DELTA up, rstk + 2 * t4 + delta_offs, t0,t6, t1
UPDATE_DELTA dn, rstk + 2 * t5 + delta_offs, t0,t7, t1
cmp t8d, t2d
%else
lea t1d, [t0d + 1]
cmp t4d, t2d
cmovnz t0d, t1d ; bit 0 -- not horz line
mov t6d, t2d
and t6d, 63 ; dn_pos
shr t2d, 6 ; dn
UPDATE_DELTA dn, rstk + 2 * t2 + delta_offs, t0,t6, t1
CALC_RES_ADDR %1, t2, t1
mov dn_addr, t2
mov dn_pos, t6d
mov t6d, t4d
and t6d, 63 ; up_pos
shr t4d, 6 ; up
UPDATE_DELTA up, rstk + 2 * t4 + delta_offs, t0,t6, t1
test t0d, 1
%endif
jz .end_line_loop
%if ARCH_X86_64
movsxd t8, dword [t3 + line.a]
movsxd t9, dword [t3 + line.b]
mov t10, [t3 + line.c]
sar t10, 7 + %1 ; c >> (tile_order + 7)
movsxd t0, dword [t3 + line.scale]
mov t1, 1 << (45 + %1)
imul t8, t0
add t8, t1
sar t8, 46 + %1 ; a
imul t9, t0
add t9, t1
sar t9, 46 + %1 ; b
imul t10, t0
shr t1, 1 + %1
add t10, t1
sar t10, 45 ; c
%else
mov r0d, [t3 + line.c]
mov r2d, [t3 + line.c + 4]
mov r1d, [t3 + line.scale]
shr r0d, 7 + %1
shl r2d, 25 - %1
or r0d, r2d ; r0d (eax) = c >> (tile_order + 7)
imul r1d ; r2d (edx) = (c >> ...) * scale >> 32
add r2d, 1 << 12
sar r2d, 13
mov t10d, r2d ; c
mov r0d, [t3 + line.b] ; r0d (eax)
imul r1d ; r2d (edx) = b * scale >> 32
add r2d, 1 << (13 + %1)
sar r2d, 14 + %1
mov r0d, [t3 + line.a] ; r0d (eax)
mov t9d, r2d ; b (overrides t3)
imul r1d ; r2d (edx) = a * scale >> 32
add r2d, 1 << (13 + %1)
sar r2d, 14 + %1 ; a (t8d)
%endif
mov t0d, t8d ; a
sar t0d, 1
sub t10d, t0d
mov t0d, t9d ; b
imul t0d, t4d
sub t10d, t0d
BCASTW m_c, t10d
BCASTW 0, t8d
%if a_shift
psllw mm_van, m0, a_shift ; a * (mmsize / 2)
%endif
pmullw m0, mm_index
psubw mm_c, m0 ; c - a * i
mov t0d, t8d ; a
sar t0d, 31
xor t8d, t0d
sub t8d, t0d ; abs_a
mov t0d, t9d ; b
mov t10d, t9d
sar t0d, 31
xor t10d, t0d
sub t10d, t0d ; abs_b
%if ARCH_X86_64 == 0
shl t10d, 16
or t8d, t10d ; abs_ab
%endif
CALC_RES_ADDR %1, t4, t0
%if ARCH_X86_64
CALC_RES_ADDR %1, t5, t0, skip
%endif
cmp t4, dn_addr
jz .single_line
%if ARCH_X86_64 || a_shift == 0
CALC_VBA %1, t9
%endif
test t6d, t6d
jz .generic_fist
mov t2d, 64
sub t2d, t6d ; 64 - up_pos
add t6d, 64 ; 64 + up_pos
FILL_BORDER_LINE %1, t4,t8,t9,t10,t2,t6, t0,t1, 0,1,2,3,4,5
%if ARCH_X86_64 == 0
mov t5, dn_addr
%if a_shift
CALC_VBA %1, t9
%endif
%endif
psubw mm_c, mm_vba
add t4, 2 << %1
cmp t4, t5
jge .end_loop
%if ARCH_X86_64 == 0
jmp .bulk_fill
%endif
.generic_fist:
%if ARCH_X86_64 == 0
mov t5, dn_addr
%if a_shift
CALC_VBA %1, t9
%endif
%endif
.bulk_fill:
mov t2d, 1 << (13 - %1)
mov t0d, t9d ; b
sar t0d, 1
sub t2d, t0d ; base
%if ARCH_X86_64
mov t0d, t10d ; abs_b
cmp t0d, t8d ; abs_a
cmovg t0d, t8d
%else
mov t0d, t8d ; abs_ab
shr t0d, 16 ; abs_b
cmp t0w, t8w
cmovg t0w, t8w
%endif
add t0d, 2
sar t0d, 2 ; dc
%if ARCH_X86_64
sub t2d, t0d ; base - dc
%else
sub t2w, t0w ; base - dc
%endif
add t0d, t0d ; 2 * dc
BCASTW 2, t0d
%if ARCH_X86_64
BCASTW 3, t2d
paddw mm_c, m3
%else
BCASTW 0, t2d
paddw mm_c, m0
mova mm_full, [words_tile%2]
%endif
.internal_loop:
%assign i 0
%rep (2 << %1) / mmsize
%if i
psubw mm_c, mm_van
%endif
CALC_LINE %1, 0, m_c,2, m_zero,m_full, 1
paddw m0, [t4 + i]
mova [t4 + i], m0
%assign i i + mmsize
%endrep
psubw mm_c, mm_vba
add t4, 2 << %1
cmp t4, t5
jl .internal_loop
%if ARCH_X86_64
psubw mm_c, m3
%else
BCASTW 0, t2d
psubw mm_c, m0
%endif
.end_loop:
%if ARCH_X86_64
test t7d, t7d
jz .end_line_loop
xor t6d, t6d
%else
mov t2d, dn_pos
test t2d, t2d
jz .end_line_loop
mov t6d, t2d
jmp .last_line
%endif
.single_line:
%if ARCH_X86_64 == 0
mov t7d, dn_pos
%endif
mov t2d, t7d
sub t2d, t6d ; dn_pos - up_pos
add t6d, t7d ; dn_pos + up_pos
.last_line:
FILL_BORDER_LINE %1, t4,t8,t9,t10,t2,t6, t0,t1, 0,1,2,3,4,5
.end_line_loop:
%if ARCH_X86_64
add r2, line_size
sub r3, 1
%else
sub dword r3m, 1
%endif
jnz .line_loop
%if ARCH_X86_64 == 0
mov r0, r0m
mov r1, r1m
%endif
GET_RES_ADDR r2
lea r3, [rstk + delta_offs]
SAVE_RESULT %1, r0,r1,r2,r3, r4,t2, 0,1,2,3
ADD rstk, alloc_size
RET
%endmacro
INIT_XMM sse2
FILL_GENERIC_TILE 4,16
FILL_GENERIC_TILE 5,32
INIT_YMM avx2
FILL_GENERIC_TILE 4,16
FILL_GENERIC_TILE 5,32

85
libass/x86/utils.asm Normal file
View File

@ -0,0 +1,85 @@
;******************************************************************************
;* utils.asm: helper macros
;******************************************************************************
;* Copyright (C) 2014 Vabishchevich Nikolay <vabnick@gmail.com>
;*
;* This file is part of libass.
;*
;* Permission to use, copy, modify, and distribute this software for any
;* purpose with or without fee is hereby granted, provided that the above
;* copyright notice and this permission notice appear in all copies.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
;******************************************************************************
%include "x86/x86inc.asm"
;------------------------------------------------------------------------------
; MUL 1:reg, 2:num
; Multiply by constant
;------------------------------------------------------------------------------
%macro MUL 2
%if (%2) == 0
xor %1, %1
%elif (%2) == 1
%elif (%2) == 2
add %1, %1 ; lea %1, [%1 + %1]
%elif (%2) == 3
lea %1, [%1 + 2 * %1]
%elif (%2) == 4
lea %1, [4 * %1] ; shl %1, 2
%elif (%2) == 5
lea %1, [%1 + 4 * %1]
%elif (%2) == 8
lea %1, [8 * %1] ; shl %1, 3
%elif (%2) == 9
lea %1, [%1 + 8 * %1]
%elif (%2) == 16
shl %1, 4
%elif (%2) == 32
shl %1, 5
%elif (%2) == 64
shl %1, 6
%elif (%2) == 128
shl %1, 7
%elif (%2) == 256
shl %1, 8
%else
imul %1, %2
%endif
%endmacro
;------------------------------------------------------------------------------
; BCASTW 1:m_dst, 2:r_src
;------------------------------------------------------------------------------
%macro BCASTW 2
movd xm%1, %2
%if mmsize == 32
vpbroadcastw m%1, xm%1
%elif mmsize == 16
punpcklwd m%1, m%1
pshufd m%1, m%1, q0000
%endif
%endmacro
;------------------------------------------------------------------------------
; PABSW 1:m_reg, 2:m_tmp
;------------------------------------------------------------------------------
%macro PABSW 2
%if cpuflag(ssse3)
pabsw m%1, m%1
%else
pxor m%2, m%2
psubw m%2, m%1
pmaxsw m%1, m%2
%endif
%endmacro

1552
libass/x86/x86inc.asm Normal file

File diff suppressed because it is too large Load Diff

11147
ltmain.sh Normal file

File diff suppressed because it is too large Load Diff

8369
m4/libtool.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

437
m4/ltoptions.m4 vendored Normal file
View File

@ -0,0 +1,437 @@
# Helper functions for option handling. -*- Autoconf -*-
#
# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 8 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
# ------------------------------------------
m4_define([_LT_MANGLE_OPTION],
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
# ---------------------------------------
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
# saved as a flag.
m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
[m4_warning([Unknown $1 option '$2'])])[]dnl
])
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
# ------------------------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
m4_define([_LT_IF_OPTION],
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
# -------------------------------------------------------
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
# are set.
m4_define([_LT_UNLESS_OPTIONS],
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
[m4_define([$0_found])])])[]dnl
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
])[]dnl
])
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
# ----------------------------------------
# OPTION-LIST is a space-separated list of Libtool options associated
# with MACRO-NAME. If any OPTION has a matching handler declared with
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
# the unknown option and exit.
m4_defun([_LT_SET_OPTIONS],
[# Set options
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[_LT_SET_OPTION([$1], _LT_Option)])
m4_if([$1],[LT_INIT],[
dnl
dnl Simply set some default values (i.e off) if boolean options were not
dnl specified:
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
])
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
])
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
[_LT_ENABLE_FAST_INSTALL])
_LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
[_LT_WITH_AIX_SONAME([aix])])
])
])# _LT_SET_OPTIONS
## --------------------------------- ##
## Macros to handle LT_INIT options. ##
## --------------------------------- ##
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
# -----------------------------------------
m4_define([_LT_MANGLE_DEFUN],
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
# -----------------------------------------------
m4_define([LT_OPTION_DEFINE],
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
])# LT_OPTION_DEFINE
# dlopen
# ------
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
])
AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
# win32-dll
# ---------
# Declare package support for building win32 dll's.
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
[enable_win32_dll=yes
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
AC_CHECK_TOOL(AS, as, false)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
AC_CHECK_TOOL(OBJDUMP, objdump, false)
;;
esac
test -z "$AS" && AS=as
_LT_DECL([], [AS], [1], [Assembler program])dnl
test -z "$DLLTOOL" && DLLTOOL=dlltool
_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
test -z "$OBJDUMP" && OBJDUMP=objdump
_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
])# win32-dll
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
# implement the --enable-shared flag, and supports the 'shared' and
# 'disable-shared' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
_LT_DECL([build_libtool_libs], [enable_shared], [0],
[Whether or not to build shared libraries])
])# _LT_ENABLE_SHARED
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
# Old names:
AC_DEFUN([AC_ENABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
])
AC_DEFUN([AC_DISABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], [disable-shared])
])
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
# implement the --enable-static flag, and support the 'static' and
# 'disable-static' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
_LT_DECL([build_old_libs], [enable_static], [0],
[Whether or not to build static libraries])
])# _LT_ENABLE_STATIC
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
# Old names:
AC_DEFUN([AC_ENABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
])
AC_DEFUN([AC_DISABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], [disable-static])
])
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
# implement the --enable-fast-install flag, and support the 'fast-install'
# and 'disable-fast-install' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
_LT_DECL([fast_install], [enable_fast_install], [0],
[Whether or not to optimize for fast installation])dnl
])# _LT_ENABLE_FAST_INSTALL
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
# Old names:
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
# _LT_WITH_AIX_SONAME([DEFAULT])
# ----------------------------------
# implement the --with-aix-soname flag, and support the `aix-soname=aix'
# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
m4_define([_LT_WITH_AIX_SONAME],
[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
shared_archive_member_spec=
case $host,$enable_shared in
power*-*-aix[[5-9]]*,yes)
AC_MSG_CHECKING([which variant of shared library versioning to provide])
AC_ARG_WITH([aix-soname],
[AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
[shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
[case $withval in
aix|svr4|both)
;;
*)
AC_MSG_ERROR([Unknown argument to --with-aix-soname])
;;
esac
lt_cv_with_aix_soname=$with_aix_soname],
[AC_CACHE_VAL([lt_cv_with_aix_soname],
[lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
with_aix_soname=$lt_cv_with_aix_soname])
AC_MSG_RESULT([$with_aix_soname])
if test aix != "$with_aix_soname"; then
# For the AIX way of multilib, we name the shared archive member
# based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
# and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
# Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
# the AIX toolchain works better with OBJECT_MODE set (default 32).
if test 64 = "${OBJECT_MODE-32}"; then
shared_archive_member_spec=shr_64
else
shared_archive_member_spec=shr
fi
fi
;;
*)
with_aix_soname=aix
;;
esac
_LT_DECL([], [shared_archive_member_spec], [0],
[Shared archive member basename, for filename based shared library versioning on AIX])dnl
])# _LT_WITH_AIX_SONAME
LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
# _LT_WITH_PIC([MODE])
# --------------------
# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
# LT_INIT options.
# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
[AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
[lt_p=${PACKAGE-default}
case $withval in
yes|no) pic_mode=$withval ;;
*)
pic_mode=default
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for lt_pkg in $withval; do
IFS=$lt_save_ifs
if test "X$lt_pkg" = "X$lt_p"; then
pic_mode=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[pic_mode=m4_default([$1], [default])])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
# Old name:
AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
## ----------------- ##
## LTDL_INIT Options ##
## ----------------- ##
m4_define([_LTDL_MODE], [])
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
[m4_define([_LTDL_MODE], [nonrecursive])])
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
[m4_define([_LTDL_MODE], [recursive])])
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
[m4_define([_LTDL_MODE], [subproject])])
m4_define([_LTDL_TYPE], [])
LT_OPTION_DEFINE([LTDL_INIT], [installable],
[m4_define([_LTDL_TYPE], [installable])])
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
[m4_define([_LTDL_TYPE], [convenience])])

124
m4/ltsugar.m4 vendored Normal file
View File

@ -0,0 +1,124 @@
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59, which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
[$5],
[$6])])
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
# --------------------------------------------------------------
m4_define([lt_dict_filter],
[m4_if([$5], [], [],
[lt_join(m4_quote(m4_default([$4], [[, ]])),
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
])

23
m4/ltversion.m4 vendored Normal file
View File

@ -0,0 +1,23 @@
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# @configure_input@
# serial 4179 ltversion.m4
# This file is part of GNU Libtool
m4_define([LT_PACKAGE_VERSION], [2.4.6])
m4_define([LT_PACKAGE_REVISION], [2.4.6])
AC_DEFUN([LTVERSION_VERSION],
[macro_version='2.4.6'
macro_revision='2.4.6'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])

99
m4/lt~obsolete.m4 vendored Normal file
View File

@ -0,0 +1,99 @@
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 5 lt~obsolete.m4
# These exist entirely to fool aclocal when bootstrapping libtool.
#
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
# using a macro with the same name in our local m4/libtool.m4 it'll
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
# and doesn't know about Autoconf macros at all.)
#
# So we provide this file, which has a silly filename so it's always
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
# we give up compatibility with versions before 1.7, at which point
# we need to keep only those names which we still refer to.
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])

215
missing Executable file
View File

@ -0,0 +1,215 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU programs.
scriptversion=2013-10-28.13; # UTC
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try '$0 --help' for more information"
exit 1
fi
case $1 in
--is-lightweight)
# Used by our autoconf macros to check whether the available missing
# script is modern enough.
exit 0
;;
--run)
# Back-compat with the calling convention used by older automake.
shift
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
to PROGRAM being missing or too old.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal autoconf autoheader autom4te automake makeinfo
bison yacc flex lex help2man
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
'g' are ignored when checking the name.
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: unknown '$1' option"
echo 1>&2 "Try '$0 --help' for more information"
exit 1
;;
esac
# Run the given program, remember its exit status.
"$@"; st=$?
# If it succeeded, we are done.
test $st -eq 0 && exit 0
# Also exit now if we it failed (or wasn't found), and '--version' was
# passed; such an option is passed most likely to detect whether the
# program is present and works.
case $2 in --version|--help) exit $st;; esac
# Exit code 63 means version mismatch. This often happens when the user
# tries to use an ancient version of a tool on a file that requires a
# minimum version.
if test $st -eq 63; then
msg="probably too old"
elif test $st -eq 127; then
# Program was missing.
msg="missing on your system"
else
# Program was found and executed, but failed. Give up.
exit $st
fi
perl_URL=http://www.perl.org/
flex_URL=http://flex.sourceforge.net/
gnu_software_URL=http://www.gnu.org/software
program_details ()
{
case $1 in
aclocal|automake)
echo "The '$1' program is part of the GNU Automake package:"
echo "<$gnu_software_URL/automake>"
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/autoconf>"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
autoconf|autom4te|autoheader)
echo "The '$1' program is part of the GNU Autoconf package:"
echo "<$gnu_software_URL/autoconf/>"
echo "It also requires GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
esac
}
give_advice ()
{
# Normalize program name to check for.
normalized_program=`echo "$1" | sed '
s/^gnu-//; t
s/^gnu//; t
s/^g//; t'`
printf '%s\n' "'$1' is $msg."
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
case $normalized_program in
autoconf*)
echo "You should only need it if you modified 'configure.ac',"
echo "or m4 files included by it."
program_details 'autoconf'
;;
autoheader*)
echo "You should only need it if you modified 'acconfig.h' or"
echo "$configure_deps."
program_details 'autoheader'
;;
automake*)
echo "You should only need it if you modified 'Makefile.am' or"
echo "$configure_deps."
program_details 'automake'
;;
aclocal*)
echo "You should only need it if you modified 'acinclude.m4' or"
echo "$configure_deps."
program_details 'aclocal'
;;
autom4te*)
echo "You might have modified some maintainer files that require"
echo "the 'autom4te' program to be rebuilt."
program_details 'autom4te'
;;
bison*|yacc*)
echo "You should only need it if you modified a '.y' file."
echo "You may want to install the GNU Bison package:"
echo "<$gnu_software_URL/bison/>"
;;
lex*|flex*)
echo "You should only need it if you modified a '.l' file."
echo "You may want to install the Fast Lexical Analyzer package:"
echo "<$flex_URL>"
;;
help2man*)
echo "You should only need it if you modified a dependency" \
"of a man page."
echo "You may want to install the GNU Help2man package:"
echo "<$gnu_software_URL/help2man/>"
;;
makeinfo*)
echo "You should only need it if you modified a '.texi' file, or"
echo "any other file indirectly affecting the aspect of the manual."
echo "You might want to install the Texinfo package:"
echo "<$gnu_software_URL/texinfo/>"
echo "The spurious makeinfo call might also be the consequence of"
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
echo "want to install GNU make:"
echo "<$gnu_software_URL/make/>"
;;
*)
echo "You might have modified some files without having the proper"
echo "tools for further handling them. Check the 'README' file, it"
echo "often tells you about the needed prerequisites for installing"
echo "this package. You may also peek at any GNU archive site, in"
echo "case some other package contains this missing '$1' program."
;;
esac
}
give_advice "$1" | sed -e '1s/^/WARNING: /' \
-e '2,$s/^/ /' >&2
# Propagate the correct exit status (expected to be 127 for a program
# not found, 63 for a program that failed due to version mismatch).
exit $st
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

7
profile/Makefile.am Normal file
View File

@ -0,0 +1,7 @@
AM_CFLAGS = -Wall
noinst_PROGRAMS = profile
profile_SOURCES = profile.c
profile_CPPFLAGS = -I$(top_srcdir)/libass
profile_LDADD = $(top_builddir)/libass/.libs/libass.a
profile_LDFLAGS = $(AM_LDFLAGS) -static

625
profile/Makefile.in Normal file
View File

@ -0,0 +1,625 @@
# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
noinst_PROGRAMS = profile$(EXEEXT)
subdir = profile
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
PROGRAMS = $(noinst_PROGRAMS)
am_profile_OBJECTS = profile-profile.$(OBJEXT)
profile_OBJECTS = $(am_profile_OBJECTS)
profile_DEPENDENCIES = $(top_builddir)/libass/.libs/libass.a
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
profile_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(profile_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(profile_SOURCES)
DIST_SOURCES = $(profile_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AS = @AS@
ASFLAGS = @ASFLAGS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCAS = @CCAS@
CCASDEPMODE = @CCASDEPMODE@
CCASFLAGS = @CCASFLAGS@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
FREETYPE_LIBS = @FREETYPE_LIBS@
FRIBIDI_CFLAGS = @FRIBIDI_CFLAGS@
FRIBIDI_LIBS = @FRIBIDI_LIBS@
GREP = @GREP@
HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
LIBPNG_LIBS = @LIBPNG_LIBS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
PKG_LIBS_DEFAULT = @PKG_LIBS_DEFAULT@
PKG_LIBS_PRIVATE = @PKG_LIBS_PRIVATE@
PKG_REQUIRES_DEFAULT = @PKG_REQUIRES_DEFAULT@
PKG_REQUIRES_PRIVATE = @PKG_REQUIRES_PRIVATE@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
nasm_check = @nasm_check@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AM_CFLAGS = -Wall
profile_SOURCES = profile.c
profile_CPPFLAGS = -I$(top_srcdir)/libass
profile_LDADD = $(top_builddir)/libass/.libs/libass.a
profile_LDFLAGS = $(AM_LDFLAGS) -static
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu profile/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu profile/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
clean-noinstPROGRAMS:
@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
echo " rm -f" $$list; \
rm -f $$list || exit $$?; \
test -n "$(EXEEXT)" || exit 0; \
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
profile$(EXEEXT): $(profile_OBJECTS) $(profile_DEPENDENCIES) $(EXTRA_profile_DEPENDENCIES)
@rm -f profile$(EXEEXT)
$(AM_V_CCLD)$(profile_LINK) $(profile_OBJECTS) $(profile_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile-profile.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
profile-profile.o: profile.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(profile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profile-profile.o -MD -MP -MF $(DEPDIR)/profile-profile.Tpo -c -o profile-profile.o `test -f 'profile.c' || echo '$(srcdir)/'`profile.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/profile-profile.Tpo $(DEPDIR)/profile-profile.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profile.c' object='profile-profile.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(profile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profile-profile.o `test -f 'profile.c' || echo '$(srcdir)/'`profile.c
profile-profile.obj: profile.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(profile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profile-profile.obj -MD -MP -MF $(DEPDIR)/profile-profile.Tpo -c -o profile-profile.obj `if test -f 'profile.c'; then $(CYGPATH_W) 'profile.c'; else $(CYGPATH_W) '$(srcdir)/profile.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/profile-profile.Tpo $(DEPDIR)/profile-profile.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profile.c' object='profile-profile.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(profile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profile-profile.obj `if test -f 'profile.c'; then $(CYGPATH_W) 'profile.c'; else $(CYGPATH_W) '$(srcdir)/profile.c'; fi`
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
installdirs:
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am:
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

99
profile/profile.c Normal file
View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "../libass/ass.h"
typedef struct image_s {
int width, height, stride;
unsigned char *buffer; // RGB24
} image_t;
ASS_Library *ass_library;
ASS_Renderer *ass_renderer;
void msg_callback(int level, const char *fmt, va_list va, void *data)
{
if (level > 6)
return;
printf("libass: ");
vprintf(fmt, va);
printf("\n");
}
static void init(int frame_w, int frame_h)
{
ass_library = ass_library_init();
if (!ass_library) {
printf("ass_library_init failed!\n");
exit(1);
}
ass_set_message_cb(ass_library, msg_callback, NULL);
ass_renderer = ass_renderer_init(ass_library);
if (!ass_renderer) {
printf("ass_renderer_init failed!\n");
exit(1);
}
ass_set_frame_size(ass_renderer, frame_w, frame_h);
ass_set_fonts(ass_renderer, NULL, "Sans", 1, NULL, 1);
}
int main(int argc, char *argv[])
{
const int frame_w = 1280;
const int frame_h = 720;
if (argc < 5) {
printf("usage: %s <subtitle file> <start time> <fps> <time to render>\n", argv[0]);
exit(1);
}
char *subfile = argv[1];
double tm = strtod(argv[2], 0);
double fps = strtod(argv[3], 0);
double time = strtod(argv[4], 0);
if(fps == 0){
printf("fps cannot equal 0\n");
exit(1);
}
init(frame_w, frame_h);
ASS_Track *track = ass_read_file(ass_library, subfile, NULL);
if (!track) {
printf("track init failed!\n");
exit(1);
}
while(tm < time){
ass_render_frame(ass_renderer, track, (int) (tm * 1000), NULL);
tm += 1 / fps;
}
ass_free_track(track);
ass_renderer_done(ass_renderer);
ass_library_done(ass_library);
return 0;
}

7
test/Makefile.am Normal file
View File

@ -0,0 +1,7 @@
AM_CFLAGS = -Wall
noinst_PROGRAMS = test
test_SOURCES = test.c
test_CPPFLAGS = -I$(top_srcdir)/libass
test_LDADD = $(top_builddir)/libass/.libs/libass.a
test_LDFLAGS = $(AM_LDFLAGS) $(LIBPNG_LIBS) -static

625
test/Makefile.in Normal file
View File

@ -0,0 +1,625 @@
# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
noinst_PROGRAMS = test$(EXEEXT)
subdir = test
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
PROGRAMS = $(noinst_PROGRAMS)
am_test_OBJECTS = test-test.$(OBJEXT)
test_OBJECTS = $(am_test_OBJECTS)
test_DEPENDENCIES = $(top_builddir)/libass/.libs/libass.a
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(test_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(test_SOURCES)
DIST_SOURCES = $(test_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AS = @AS@
ASFLAGS = @ASFLAGS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCAS = @CCAS@
CCASDEPMODE = @CCASDEPMODE@
CCASFLAGS = @CCASFLAGS@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
FREETYPE_LIBS = @FREETYPE_LIBS@
FRIBIDI_CFLAGS = @FRIBIDI_CFLAGS@
FRIBIDI_LIBS = @FRIBIDI_LIBS@
GREP = @GREP@
HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
LIBPNG_LIBS = @LIBPNG_LIBS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
PKG_LIBS_DEFAULT = @PKG_LIBS_DEFAULT@
PKG_LIBS_PRIVATE = @PKG_LIBS_PRIVATE@
PKG_REQUIRES_DEFAULT = @PKG_REQUIRES_DEFAULT@
PKG_REQUIRES_PRIVATE = @PKG_REQUIRES_PRIVATE@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
nasm_check = @nasm_check@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AM_CFLAGS = -Wall
test_SOURCES = test.c
test_CPPFLAGS = -I$(top_srcdir)/libass
test_LDADD = $(top_builddir)/libass/.libs/libass.a
test_LDFLAGS = $(AM_LDFLAGS) $(LIBPNG_LIBS) -static
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu test/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu test/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
clean-noinstPROGRAMS:
@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
echo " rm -f" $$list; \
rm -f $$list || exit $$?; \
test -n "$(EXEEXT)" || exit 0; \
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
test$(EXEEXT): $(test_OBJECTS) $(test_DEPENDENCIES) $(EXTRA_test_DEPENDENCIES)
@rm -f test$(EXEEXT)
$(AM_V_CCLD)$(test_LINK) $(test_OBJECTS) $(test_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-test.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
test-test.o: test.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test-test.o -MD -MP -MF $(DEPDIR)/test-test.Tpo -c -o test-test.o `test -f 'test.c' || echo '$(srcdir)/'`test.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test-test.Tpo $(DEPDIR)/test-test.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test.c' object='test-test.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test-test.o `test -f 'test.c' || echo '$(srcdir)/'`test.c
test-test.obj: test.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test-test.obj -MD -MP -MF $(DEPDIR)/test-test.Tpo -c -o test-test.obj `if test -f 'test.c'; then $(CYGPATH_W) 'test.c'; else $(CYGPATH_W) '$(srcdir)/test.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test-test.Tpo $(DEPDIR)/test-test.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test.c' object='test-test.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test-test.obj `if test -f 'test.c'; then $(CYGPATH_W) 'test.c'; else $(CYGPATH_W) '$(srcdir)/test.c'; fi`
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
installdirs:
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am:
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

228
test/test.c Normal file
View File

@ -0,0 +1,228 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "../libass/ass.h"
#include <png.h>
typedef struct image_s {
int width, height, stride;
unsigned char *buffer; // RGB24
} image_t;
ASS_Library *ass_library;
ASS_Renderer *ass_renderer;
void msg_callback(int level, const char *fmt, va_list va, void *data)
{
if (level > 6)
return;
printf("libass: ");
vprintf(fmt, va);
printf("\n");
}
static void write_png(char *fname, image_t *img)
{
FILE *fp;
png_structp png_ptr;
png_infop info_ptr;
png_byte **row_pointers;
int k;
png_ptr =
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info_ptr = png_create_info_struct(png_ptr);
fp = NULL;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return;
}
fp = fopen(fname, "wb");
if (fp == NULL) {
printf("PNG Error opening %s for writing!\n", fname);
return;
}
png_init_io(png_ptr, fp);
png_set_compression_level(png_ptr, 0);
png_set_IHDR(png_ptr, info_ptr, img->width, img->height,
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
png_set_bgr(png_ptr);
row_pointers = (png_byte **) malloc(img->height * sizeof(png_byte *));
for (k = 0; k < img->height; k++)
row_pointers[k] = img->buffer + img->stride * k;
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
free(row_pointers);
fclose(fp);
}
static void init(int frame_w, int frame_h)
{
ass_library = ass_library_init();
if (!ass_library) {
printf("ass_library_init failed!\n");
exit(1);
}
ass_set_message_cb(ass_library, msg_callback, NULL);
ass_renderer = ass_renderer_init(ass_library);
if (!ass_renderer) {
printf("ass_renderer_init failed!\n");
exit(1);
}
ass_set_frame_size(ass_renderer, frame_w, frame_h);
ass_set_fonts(ass_renderer, NULL, "sans-serif",
ASS_FONTPROVIDER_AUTODETECT, NULL, 1);
}
static image_t *gen_image(int width, int height)
{
image_t *img = malloc(sizeof(image_t));
img->width = width;
img->height = height;
img->stride = width * 3;
img->buffer = (unsigned char *) calloc(1, height * width * 3);
memset(img->buffer, 63, img->stride * img->height);
//for (int i = 0; i < height * width * 3; ++i)
// img->buffer[i] = (i/3/50) % 100;
return img;
}
#define _r(c) ((c)>>24)
#define _g(c) (((c)>>16)&0xFF)
#define _b(c) (((c)>>8)&0xFF)
#define _a(c) ((c)&0xFF)
static void blend_single(image_t * frame, ASS_Image *img)
{
int x, y;
unsigned char opacity = 255 - _a(img->color);
unsigned char r = _r(img->color);
unsigned char g = _g(img->color);
unsigned char b = _b(img->color);
unsigned char *src;
unsigned char *dst;
src = img->bitmap;
dst = frame->buffer + img->dst_y * frame->stride + img->dst_x * 3;
for (y = 0; y < img->h; ++y) {
for (x = 0; x < img->w; ++x) {
unsigned k = ((unsigned) src[x]) * opacity / 255;
// possible endianness problems
dst[x * 3] = (k * b + (255 - k) * dst[x * 3]) / 255;
dst[x * 3 + 1] = (k * g + (255 - k) * dst[x * 3 + 1]) / 255;
dst[x * 3 + 2] = (k * r + (255 - k) * dst[x * 3 + 2]) / 255;
}
src += img->stride;
dst += frame->stride;
}
}
static void blend(image_t * frame, ASS_Image *img)
{
int cnt = 0;
while (img) {
blend_single(frame, img);
++cnt;
img = img->next;
}
printf("%d images blended\n", cnt);
}
char *font_provider_labels[] = {
[ASS_FONTPROVIDER_NONE] = "None",
[ASS_FONTPROVIDER_AUTODETECT] = "Autodetect",
[ASS_FONTPROVIDER_CORETEXT] = "CoreText",
[ASS_FONTPROVIDER_FONTCONFIG] = "Fontconfig",
[ASS_FONTPROVIDER_DIRECTWRITE]= "DirectWrite",
};
static void print_font_providers(ASS_Library *ass_library)
{
int i;
ASS_DefaultFontProvider *providers;
size_t providers_size = 0;
ass_get_available_font_providers(ass_library, &providers, &providers_size);
printf("test.c: Available font providers (%zu): ", providers_size);
for (i = 0; i < providers_size; i++) {
const char *separator = i > 0 ? ", ": "";
printf("%s'%s'", separator, font_provider_labels[providers[i]]);
}
printf(".\n");
free(providers);
}
int main(int argc, char *argv[])
{
const int frame_w = 1280;
const int frame_h = 720;
if (argc < 4) {
printf("usage: %s <image file> <subtitle file> <time>\n", argv[0]);
exit(1);
}
char *imgfile = argv[1];
char *subfile = argv[2];
double tm = strtod(argv[3], 0);
print_font_providers(ass_library);
init(frame_w, frame_h);
ASS_Track *track = ass_read_file(ass_library, subfile, NULL);
if (!track) {
printf("track init failed!\n");
return 1;
}
ASS_Image *img =
ass_render_frame(ass_renderer, track, (int) (tm * 1000), NULL);
image_t *frame = gen_image(frame_w, frame_h);
blend(frame, img);
ass_free_track(track);
ass_renderer_done(ass_renderer);
ass_library_done(ass_library);
write_png(imgfile, frame);
free(frame->buffer);
free(frame);
return 0;
}