pax_global_header00006660000000000000000000000064145323604610014516gustar00rootroot0000000000000052 comment=96e424a6c8c7916a6afde0fd0c989ae68f412b52 rl-renderpm-4.0.3/000077500000000000000000000000001453236046100137515ustar00rootroot00000000000000rl-renderpm-4.0.3/LICENSE000066400000000000000000000032531453236046100147610ustar00rootroot00000000000000##################################################################################### # # Copyright (c) 2000-2022, ReportLab Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the company nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE OFFICERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # ##################################################################################### rl-renderpm-4.0.3/MANIFEST.in000066400000000000000000000000461453236046100155070ustar00rootroot00000000000000include README.* LICENSE test/* src/* rl-renderpm-4.0.3/PKG-INFO000066400000000000000000000033111453236046100150440ustar00rootroot00000000000000Metadata-Version: 2.1 Name: rl_renderPM Version: 4.0.3 Summary: Bitmap Render Acclerator for ReportLab Home-page: http://www.reportlab.com/ Author: Andy Robinson, Robin Becker, the ReportLab team and the community Author-email: reportlab-users@lists2.reportlab.com License: BSD license (see LICENSE.txt for details), Copyright (c) 2000-2022, ReportLab Inc. Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Topic :: Printing Classifier: Topic :: Text Processing :: Markup Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.7,<4 Description-Content-Type: text/x-rst ``rl_renderPM`` is a package containing the ReportLab accelerator module ``_renderPM`` which can be used to speedup the ``reportlab.graphics.renderPM`` functions. The python bitmap render module ``reportlab.graphics.renderPM`` can either use ``rlPyCairo``, ``pycairo`` and ``freetype-py`` or ``rl_renderPM`` + built in ``freetype`` libraries. The choice is made by overriding the ``reportlab.rl_settings`` module value ``_renderPMBackend`` using one of the settings files ``reportlab/local_reportlab_settings.py``, ``reportlab_settings.py`` or ``~/.reportlab_settings``, which are searched for in that order. The default value of ``renderPMBackend`` is ``'rlPyCairo'``, but it can be set to ``'_renderPM'`` to use this extension which is based on an older library ``libart_lgpl``. rl-renderpm-4.0.3/README.md000066400000000000000000000014241453236046100152310ustar00rootroot00000000000000``rl_renderPM`` is a package containing the ReportLab accelerator module ``_renderPM`` which can be used to speedup the ``reportlab.graphics.renderPM`` functions. The python bitmap render module ``reportlab.graphics.renderPM`` can either use ``rlPyCairo``, ``pycairo`` and ``freetype-py`` or ``rl_renderPM`` + built in ``freetype`` libraries. The choice is made by overriding the ``reportlab.rl_settings`` module value ``_renderPMBackend`` using one of the settings files ``reportlab/local_reportlab_settings.py``, ``reportlab_settings.py`` or ``~/.reportlab_settings``, which are searched for in that order. The default value of ``renderPMBackend`` is ``'rlPyCairo'``, but it can be set to ``'_renderPM'`` to use this extension which is based on an older library ``libart_lgpl``. rl-renderpm-4.0.3/pyproject.toml000066400000000000000000000001311453236046100166600ustar00rootroot00000000000000[build-system] requires = ["setuptools","wheel"] build-backend = "setuptools.build_meta" rl-renderpm-4.0.3/setup.cfg000066400000000000000000000000461453236046100155720ustar00rootroot00000000000000[egg_info] tag_build = tag_date = 0 rl-renderpm-4.0.3/setup.py000066400000000000000000000457711453236046100155010ustar00rootroot00000000000000#Copyright ReportLab Europe Ltd. 2000-2021 #see license.txt for license details import os, sys, glob, shutil, re, sysconfig, traceback, io, subprocess from urllib.parse import quote as urlquote from wheel.bdist_wheel import bdist_wheel, get_abi_tag from setuptools import setup, Extension platform = sys.platform pjoin = os.path.join abspath = os.path.abspath normpath = os.path.normpath isfile = os.path.isfile isdir = os.path.isdir dirname = os.path.dirname basename = os.path.basename splitext = os.path.splitext addrSize = 64 if sys.maxsize > 2**32 else 32 sysconfig_platform = sysconfig.get_platform() pkgDir = os.path.realpath(dirname(__file__)) def spCall(cmd,*args,**kwds): r = subprocess.call( cmd, stderr =subprocess.STDOUT, stdout = subprocess.DEVNULL if kwds.pop('dropOutput',False) else None, timeout = kwds.pop('timeout',3600), ) if verbose>=3: print('%r --> %s' % (' '.join(cmd),r), pfx='!!!!!' if r else '#####', add=False) return r def specialOption(n,ceq=False): v = 0 while n in sys.argv: v += 1 sys.argv.remove(n) if ceq: n += '=' V = [_ for _ in sys.argv if _.startswith(n)] for _ in V: sys.argv.remove(_) if V: n = len(n) v = V[-1][n:] return v usla = specialOption('--use-system-libart') mdbg = specialOption('--memory-debug') verbose = specialOption('--verbose',ceq=True) def _packages_path(d): P = [_ for _ in sys.path if basename(_)==d] if P: return P[0] package_path = _packages_path('dist-packages') or _packages_path('site-packages') package_path = pjoin(package_path, 'reportlab') def die(msg): raise ValueError(msg) def make_libart_config(src): from struct import calcsize as sizeof L=["""/* Automatically generated by setup.py */ #ifndef _ART_CONFIG_H #\tdefine _ART_CONFIG_H #\tdefine ART_SIZEOF_CHAR %d #\tdefine ART_SIZEOF_SHORT %d #\tdefine ART_SIZEOF_INT %d #\tdefine ART_SIZEOF_LONG %d""" % (sizeof('c'), sizeof('h'), sizeof('i'), sizeof('l')) ] aL = L.append if sizeof('c')==1: aL("typedef unsigned char art_u8;") else: die("sizeof(char) != 1") if sizeof('h')==2: aL("typedef unsigned short art_u16;") else: die("sizeof(short) != 2") if sizeof('i')==4: aL("typedef unsigned int art_u32;") elif sizeof('l')==4: aL("typedef unsigned long art_u32;") else: die("sizeof(int)!=4 and sizeof(long)!=4") aL('#endif\n') with open(pjoin(src,'art_config.h'),'w') as f: f.write('\n'.join(L)) #this code from /FBot's PIL setup.py def aDir(P, d, x=None): if d and isdir(d) and d not in P: if x is None: P.append(d) else: P.insert(x, d) def findFile(root, wanted, followlinks=True): visited = set() for p, D, F in os.walk(root,followlinks=followlinks): #scan directories to check for prior visits #use dev/inode to make unique key SD = [].append for d in D: dk = os.stat(pjoin(p,d)) dk = dk.st_dev, dk.st_ino if dk not in visited: visited.add(dk) SD(d) D[:] = SD.__self__ #set the dirs to be scanned for fn in F: if fn==wanted: return abspath(pjoin(p,fn)) def freetypeVersion(fn,default='20'): with open(fn,'r') as _: text = _.read() pat = re.compile(r'^#define\s+FREETYPE_(?PMAJOR|MINOR|PATCH)\s*(?P\d*)\s*$',re.M) locmap=dict(MAJOR=0,MINOR=1,PATCH=2) loc = ['','',''] for m in pat.finditer(text): loc[locmap[m.group('level')]] = m.group('value') loc = list(filter(None,loc)) return '.'.join(loc) if loc else default class inc_lib_dirs: def __call__(self,libname=None): L = [] I = [] if platform == "cygwin": aDir(L, os.path.join("/usr/lib", "python%s" % sys.version[:3], "config")) elif platform == "darwin": machine = sysconfig_platform.split('-')[-1] if machine=='arm64' or os.environ.get('ARCHFLAGS','')=='-arch arm64': #print('!!!!! detected darwin arm64 build') #probably an M1 target = pjoin( ensureResourceStuff('m1stuff.tar.gz','m1stuff','tar', baseDir=os.environ.get('RL_CACHE_DIR','/tmp/reportlab')), 'm1stuff','opt','homebrew' ) _lib = pjoin(target,'lib') _inc = pjoin(target,'include','freetype2') aDir(L, _lib) aDir(I, _inc) #print('!!!!! L=%s I=%s' % (L,I)) elif machine=='x86_64': aDir(L,'/usr/local/lib') aDir(I, "/usr/local/include/freetype2") # attempt to make sure we pick freetype2 over other versions aDir(I, "/sw/include/freetype2") aDir(I, "/sw/lib/freetype2/include") # fink installation directories aDir(L, "/sw/lib") aDir(I, "/sw/include") # darwin ports installation directories aDir(L, "/opt/local/lib") aDir(I, "/opt/local/include") aDir(I, "/usr/local/include") aDir(L, "/usr/local/lib") aDir(I, "/usr/include") aDir(L, "/usr/lib") aDir(I, "/usr/include/freetype2") if addrSize==64: aDir(L, "/usr/lib/lib64") aDir(L, "/usr/lib/x86_64-linux-gnu") else: aDir(L, "/usr/lib/lib32") prefix = sysconfig.get_config_var("prefix") if prefix: aDir(L, pjoin(prefix, "lib")) aDir(I, pjoin(prefix, "include")) if libname: gsn = ''.join((('lib' if not libname.startswith('lib') else ''),libname,'*')) L = list(filter(lambda _: glob.glob(pjoin(_,gsn)),L)) for d in I: mif = findFile(d,'ft2build.h') if mif: #print('!!!!! d=%s --> mif=%r' % (d,mif)) break else: mif = None if mif: d = dirname(mif) I = [dirname(d), d] #fix for some RHEL systems from James Brown jbrown at easypost dot com subdir = pjoin(d,'freetype2') if isdir(subdir): I.append(subdir) ftv = freetypeVersion(findFile(d,'freetype.h'),'22') else: print('!!!!! cannot find ft2build.h') sys.exit(1) return ftv,I,L inc_lib_dirs=inc_lib_dirs() def _find_ccode(cn): fn = normpath(abspath(pjoin('src',cn))) return dirname(fn) if isfile(fn) else None def url2data(url,returnRaw=False): import urllib.request as ureq remotehandle = ureq.urlopen(url) try: raw = remotehandle.read() return raw if returnRaw else io.BytesIO(raw) finally: remotehandle.close() def ensureResourceStuff( ftpName='winstuff.zip', buildName='winstuff', extract='zip', baseDir=pjoin(pkgDir,'build'), ): url='https://www.reportlab.com/ftp/%s' % ftpName target=pjoin(baseDir,buildName) done = pjoin(target,'.done') if not isfile(done): if not isdir(target): os.makedirs(target) if extract=='zip': import zipfile zipfile.ZipFile(url2data(url), 'r').extractall(path=target) elif extract=='tar': import tarfile tarfile.open(fileobj=url2data(url), mode='r:gz').extractall(path=target) import time with open(done,'w') as _: _.write(time.strftime('%Y%m%dU%H%M%S\n',time.gmtime())) return target def canImport(pkg): ns = [pkg.find(_) for _ in '<>=' if _ in pkg] if ns: pkg =pkg[:min(ns)] ns = {} try: exec('import %s as M' % pkg,{},ns) except: if verbose>=2: showTraceback("can't import %s" % pkg) return 'M' in ns def vopt(opt): opt = '--%s=' % opt v = [_ for _ in sys.argv if _.startswith(opt)] for _ in v: sys.argv.remove(_) n = len(opt) return list(filter(None,[_[n:] for _ in v])) class QUPStr(str): def __new__(cls,s,u,p): self = str.__new__(cls,s) self.u = u self.p = p return self def qup(url, pat=re.compile(r'(?Phttps?://)(?P[^@]*)(?P@.*)$')): '''urlquote the user name and password''' m = pat.match(url) if m: u, p = m.group('up').split(':',1) url = "%s%s:%s%s" % (m.group('scheme'),urlquote(u),urlquote(p),m.group('rest')) else: u = p = '' return QUPStr(url,u,p) def showEnv(): action = -1 if specialOption('--show-env-only') else 1 if specialOption('--show-env') else 0 if not action: return print('+++++ setup.py environment') print('+++++ sys.version = %s' % sys.version.replace('\n','')) import platform for k in sorted((_ for _ in dir(platform) if not _.startswith('_'))): try: v = getattr(platform,k) if isinstance(v,(str,list,tuple,bool)): v = repr(v) if callable(v) and v.__module__=='platform': v = repr(v()) else: continue except: v = '?????' print('+++++ platform.%s = %s' % (k,v)) print('--------------------------') for k, v in sorted(os.environ.items()): print('+++++ environ[%s] = %r' % (k,v)) print('--------------------------') if action<0: sys.exit(0) def main(): showEnv() debug_compile_args = [] debug_link_args = [] debug_macros = [] debug = int(os.environ.get('RL_DEBUG','0')) if debug: if sys.platform == 'win32': debug_compile_args=['/Zi'] debug_link_args=['/DEBUG'] if debug>1: debug_macros.extend([('RL_DEBUG',debug), ('ROBIN_DEBUG',None)]) if mdbg: debug_macros.extend([('MEMORY_DEBUG',None)]) LIBRARIES=[] EXT_MODULES = [] RENDERPM = _find_ccode('_renderPM.c') print( '===================================================') print( 'Attempting build of _renderPM') print( 'extension from %r'%RENDERPM) print( '===================================================') GT1_DIR=pjoin(RENDERPM,'gt1') if not usla: LIBART_INC=None #don't use system libart else: #check for an installed libart LIBART_INC = list(sorted(glob.glob('/usr/include/libart-*/libart_lgpl/libart-features.h'))) if LIBART_INC: def installed_libart_version(fn): for l in open(fn, 'r').readlines(): if l.startswith('#define LIBART_VERSION'): v = l[:-1].split(' ')[-1] return v return '"0.0.0"' LIBART_INC = LIBART_INC[-1] LIBART_VERSION = installed_libart_version(LIBART_INC) LIBART_INC = os.path.dirname(LIBART_INC) LIBART_SOURCES=[] LIBART_LIB = ['art_lgpl_2'] print('will use installed libart %s' % LIBART_VERSION.replace('"','')) else: LIBART_DIR = LIBART_INC = pjoin(RENDERPM,'libart_lgpl') LIBART_LIB = [] make_libart_config(LIBART_DIR) LIBART_SOURCES=[ pjoin(LIBART_DIR,'art_vpath_bpath.c'), pjoin(LIBART_DIR,'art_rgb_pixbuf_affine.c'), pjoin(LIBART_DIR,'art_rgb_svp.c'), pjoin(LIBART_DIR,'art_svp.c'), pjoin(LIBART_DIR,'art_svp_vpath.c'), pjoin(LIBART_DIR,'art_svp_vpath_stroke.c'), pjoin(LIBART_DIR,'art_svp_ops.c'), pjoin(LIBART_DIR,'art_svp_wind.c'), pjoin(LIBART_DIR,'art_vpath.c'), pjoin(LIBART_DIR,'art_vpath_dash.c'), pjoin(LIBART_DIR,'art_affine.c'), pjoin(LIBART_DIR,'art_rect.c'), pjoin(LIBART_DIR,'art_rgb_affine.c'), pjoin(LIBART_DIR,'art_rgb_affine_private.c'), pjoin(LIBART_DIR,'art_rgb.c'), pjoin(LIBART_DIR,'art_rgb_rgba_affine.c'), pjoin(LIBART_DIR,'art_svp_intersect.c'), pjoin(LIBART_DIR,'art_svp_render_aa.c'), pjoin(LIBART_DIR,'art_misc.c'), ] def libart_version(): pat0 = re.compile(r'^\s*LIBART_(MAJOR|MINOR|MICRO)_VERSION\s*=\s*(\d+)') pat1 = re.compile(r'^\s*m4_define\s*\(\s*\[\s*libart_(major|minor|micro)_version\s*\]\s*,\s*\[(\d+)\]\s*\)') def check_match(l): for p in (pat0, pat1): m = p.match(l) if m: return m K = ('major','minor','micro') D = {} for l in open(pjoin(LIBART_DIR,'configure.in'),'r').readlines(): m = check_match(l) if m: D[m.group(1).lower()] = m.group(2) if len(D)==3: break return '.'.join(map(lambda k,D=D: D.get(k,'?'),K)) LIBART_VERSION = libart_version() print('will use package libart %s' % LIBART_VERSION.replace('"','')) SOURCES=[pjoin(RENDERPM,'_renderPM.c'), pjoin(GT1_DIR,'gt1-parset1.c'), pjoin(GT1_DIR,'gt1-dict.c'), pjoin(GT1_DIR,'gt1-namecontext.c'), pjoin(GT1_DIR,'gt1-region.c'), ]+LIBART_SOURCES if platform=='win32': target = ensureResourceStuff() FT_LIB = pjoin(target,'libs','amd64' if addrSize==64 else 'x86','freetype.lib') if not isfile(FT_LIB): print('freetype lib %r not found' % FT_LIB, pfx='!!!!') FT_LIB=[] if FT_LIB: FT_INC_DIR = pjoin(target,'include') if not isfile(pjoin(FT_INC_DIR,'ft2build.h')): FT_INC_DIR = pjoin(FT_INC_DIR,'freetype2') if not isfile(pjoin(FT_INC_DIR,'ft2build.h')): print('freetype2 include folder %r not found' % FT_INC_DIR, pfx='!!!!!') FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[] FT_MACROS = [('RENDERPM_FT',None)] FT_LIB_DIR = [dirname(FT_LIB)] FT_INC_DIR = [FT_INC_DIR] FT_LIB_PATH = FT_LIB FT_LIB = [splitext(basename(FT_LIB))[0]] if isdir(FT_INC_DIR[0]): print('installing with freetype %r' % FT_LIB_PATH) else: FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[] else: ftv, I, L = inc_lib_dirs('freetype') FT_LIB=['freetype'] FT_LIB_DIR=L FT_INC_DIR=I FT_MACROS = [('RENDERPM_FT',None)] print('installing with freetype version %s' % ftv) print('FT_LIB_DIR=%r FT_INC_DIR=%r' % (FT_LIB_DIR,FT_INC_DIR)) if not FT_LIB: raise ValueError('Cannot install without freetype which has not been found') def get_la_info(): limited_api_kwds = {} if specialOption('--abi3') or os.environ.get('ABI3_WHEEL','0')=='1': cpstr = get_abi_tag() if cpstr.startswith("cp"): lav = '0x03070000' cpstr = 'cp37' if sys.platform == "darwin": machine = sysconfig.get_platform().split('-')[-1] if machine=='arm64' or os.environ.get('ARCHFLAGS','')=='-arch arm64': #according to cibuildwheel/github M1 supports pythons >= 3.8 cpstr = 'cp38' lav = '0x03080000' class bdist_wheel_abi3(bdist_wheel): __cpstr = cpstr def get_tag(self): python, abi, plat = super().get_tag() if python.startswith("cp"): abi = 'abi3' python = self.__cpstr return python,abi,plat limited_api_kwds = dict( cmdclass={"bdist_wheel": bdist_wheel_abi3}, macros=[("Py_LIMITED_API", lav)], ) return limited_api_kwds limited_api_kwds = get_la_info() limited_api_macros = limited_api_kwds.pop('macros',[]) def getVersionFromCCode(fn): with open(fn,'r') as _: tag = re.search(r'^#define\s+VERSION\s+"([^"]*)"',_.read(),re.M) return tag and tag.group(1) or '' def getREADME_md(fn='README.md'): try: with open(fn,'r') as _: return _.read() except: return """This is an bitmap render accelerator module for the ReportLab Toolkit Open Source Python library for generating PDFs and graphics.""" setup( ext_modules=[ Extension( '_rl_renderPM', SOURCES, include_dirs=[RENDERPM,LIBART_INC,GT1_DIR]+FT_INC_DIR, define_macros=( FT_MACROS +[('LIBART_COMPILATION',None)] +debug_macros+[('LIBART_VERSION',LIBART_VERSION)] +limited_api_macros ), py_limited_api = limited_api_macros!=[], library_dirs=[]+FT_LIB_DIR, # libraries to link against libraries=FT_LIB+LIBART_LIB, extra_compile_args=debug_compile_args, extra_link_args=debug_link_args, ), ], name="rl_renderPM", version=getVersionFromCCode(pjoin('src','_renderPM.c')), license="BSD license (see LICENSE.txt for details), Copyright (c) 2000-2022, ReportLab Inc.", description="Bitmap Render Acclerator for ReportLab", long_description=getREADME_md(), long_description_content_type="text/x-rst", author="Andy Robinson, Robin Becker, the ReportLab team and the community", author_email="reportlab-users@lists2.reportlab.com", url="http://www.reportlab.com/", packages=[], package_data = {}, classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Topic :: Printing', 'Topic :: Text Processing :: Markup', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', ], license_files = ['LICENSE.txt'], #this probably only works for setuptools, but distutils seems to ignore it install_requires=[], python_requires='>=3.7,<4', extras_require={ }, **limited_api_kwds ) if __name__=='__main__': main() rl-renderpm-4.0.3/src/000077500000000000000000000000001453236046100145405ustar00rootroot00000000000000rl-renderpm-4.0.3/src/_renderPM.c000066400000000000000000001664171453236046100165760ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include "Python.h" #ifdef Py_LIMITED_API # define RLPyBytes_Size PyBytes_Size # define RLPyTuple_SetItem PyTuple_SetItem # define RLPyList_SetItem PyList_SetItem # define RLPyList_Size PyList_Size # define RLPyFloat_AsDouble PyFloat_AsDouble # define RLPyBytes_AsString PyBytes_AsString # define RLPyUnicode_GetLength PyUnicode_GetLength #else # define RLPyBytes_Size PyBytes_GET_SIZE # define RLPyTuple_SetItem PyTuple_SET_ITEM # define RLPyList_SetItem PyList_SET_ITEM # define RLPyList_Size PyList_GET_SIZE # define RLPyFloat_AsDouble PyFloat_AS_DOUBLE # define RLPyBytes_AsString PyBytes_AS_STRING # define RLPyUnicode_GetLength PyUnicode_GET_LENGTH #endif #include #include "libart_lgpl/libart.h" #include "gt1/gt1-parset1.h" #include "gt1/gt1-misc.h" #define FILL_UNSET -1 #define FILL_EVEN_ODD 0 #define FILL_NON_ZERO 1 #if defined(macintosh) # include # define strdup _strdup #endif #define VERSION "4.0.3" #define MODULENAME "_rl_renderPM" #define PyInt_FromLong PyLong_FromLong #define staticforward static #define statichere static PyObject *RLPy_FindMethod(PyMethodDef *ml, PyObject *self, const char* name){ for(;ml->ml_name!=NULL;ml++) if(name[0]==ml->ml_name[0] && strcmp(name+1,ml->ml_name+1)==0) return PyCFunction_New(ml, self); return NULL; } #define Py_FindMethod RLPy_FindMethod #define __STR(x) #x #define STRINGIFY(x) __STR(x) #ifndef LIBART_VERSION # define LIBART_VERSION ?.?.? #endif #ifdef RENDERPM_FT # define _FT_DOC " _rl_renderPM.ft_get_face(fontName) --> ft_face instance\n" #else # define _FT_DOC "" #endif #ifdef MEMORY_DEBUG # define _MDBG_DOC " _rl_renderPM.mtrace(int) start or stop the malloc tracing\n" #else # define _MDBG_DOC "" #endif PyDoc_STRVAR(__DOC__, "Helper extension module for renderPM.\n\ \n\ Interface summary:\n\ \n\ from reportlan.graphics import _rl_renderPM\n\ _rl_renderPM.gstate(width,height[,depth=3,bg=0xffffff]) create an \n\ initialised graphics state\n\ _rl_renderPM.makeT1Font(fontName,pfbPath,names[,reader]) make a T1 font\n\ _rl_renderPM.delCache() delete all T1 font info\n\ _rl_renderPM.pil2pict(cols,rows,datastr,palette) return PICT version of\n\ im as bytes\n" _FT_DOC _MDBG_DOC "\n\ _rl_renderPM._libart_version base library version string\n\ _rl_renderPM._version module version string ie " VERSION "\n\ "); #if PY_VERSION_HEX < 0x01060000 # define PyObject_DEL(op) PyMem_DEL((op)) #endif #define VECSP 0.25 typedef struct { int format; art_u8 * buf; int width; int height; int nchan; int rowstride; } pixBufT; typedef struct { Py_ssize_t width; Py_ssize_t height; Py_ssize_t stride; art_u8 *buf; } gstateColorX; #ifdef RENDERPM_FT #ifdef RENDERPM_PARSE_UTF8 static PyObject* parse_utf8(PyObject* self, PyObject* args) { char *c, *msg; Py_ssize_t n; int i; unsigned first, second, third; PyObject *r; if(!PyArg_ParseTuple(args, "t#:parse_utf8", &c, &n)) return NULL; i = 0; r = PyList_New(0); while(i=0xc0) goto E0; PyList_Append(r, PyInt_FromLong((((first&0x1f)<<6)|(second&0x3f)))); } else if(first<0xE0){ second = c[i++]; third = c[i++]; if(second<0x80 || second>=0xC0 || third<0x80 || third>=0xC0) goto E0; PyList_Append(r, PyInt_FromLong((((first&0x0f)<<12)|((second&0x3f)<<6)|(third&0x3f)))); } else{ msg = "UTF-8 characters outside 16-bit range not supported"; goto RL_ERROR_EXIT; } } return r; RL_ERROR_EXIT: Py_DECREF(r); PyErr_SetString(PyExc_ValueError,msg); Py_INCREF(Py_None); return Py_None; } #endif PyObject* _pdfmetrics__fonts=0; static PyObject *_get_pdfmetrics__fonts(void){ if(!_pdfmetrics__fonts){ PyObject *mod=PyImport_ImportModule("reportlab.pdfbase.pdfmetrics"); if(mod){ _pdfmetrics__fonts = PyObject_GetAttrString(mod,"_fonts"); Py_DECREF(mod); } } return _pdfmetrics__fonts; } #include #include FT_FREETYPE_H static FT_Library ft_library=0; typedef struct { PyObject_HEAD FT_Face face; } py_FT_FontObject; staticforward PyTypeObject py_FT_Font_Type; /* round a 26.6 pixel coordinate to the nearest larger integer */ #define PIXEL(x) ((((x)+63) & -64)>>6) static py_FT_FontObject *_get_ft_face(char *fontName) { int error = 1; PyObject *_fonts=_get_pdfmetrics__fonts(); PyObject *font, *face, *_data; py_FT_FontObject *ft_face; if(!_fonts) return NULL; font = PyDict_GetItemString(_fonts,fontName); if(!font) return NULL; ft_face = (py_FT_FontObject*)PyObject_GetAttrString(font,"_ft_face"); if(ft_face) return ft_face; PyErr_Clear(); if(!ft_library){ if(FT_Init_FreeType(&ft_library)){ PyErr_SetString(PyExc_IOError,"cannot initialize FreeType library"); goto RET; } } ft_face = PyObject_New(py_FT_FontObject, &py_FT_Font_Type); if(!ft_face){ PyErr_Format(PyExc_MemoryError, "Cannot allocate ft_face for TTFont %s", fontName); goto RET; } ft_face->face = NULL; face = PyObject_GetAttrString(font,"face"); if(!face) goto RET; _data = PyObject_GetAttrString(face,"_ttf_data"); Py_DECREF(face); if(!_data) goto RET; error = FT_New_Memory_Face(ft_library, (unsigned char *)PyBytes_AsString(_data), (FT_Long)RLPyBytes_Size(_data), 0, &ft_face->face); Py_DECREF(_data); if(error){ PyErr_Format(PyExc_IOError, "FT_New_Memory_Face(%s) Failed!", fontName); goto RET; } PyObject_SetAttrString(font,"_ft_face",(PyObject*)ft_face); RET:if(error && ft_face){ Py_DECREF(ft_face); ft_face = NULL; } return ft_face; } static FT_Face _ft_get_face(char *fontName) { py_FT_FontObject *ft_face = _get_ft_face(fontName); if(ft_face){ FT_Face face = ft_face->face; Py_DECREF(ft_face); return face; } return NULL; } static PyObject* ft_get_face(PyObject* self, PyObject* args) { char* fontName; if(!PyArg_ParseTuple(args,"s:ft_get_face",&fontName)) return NULL; return (PyObject*)_get_ft_face(fontName); } static void py_FT_font_dealloc(py_FT_FontObject* self) { if(self->face) FT_Done_Face(self->face); PyObject_DEL(self); } static PyObject* py_FT_font_getattr(py_FT_FontObject* self, char* name) { /* attributes */ if (!strcmp(name, "family")) return PyUnicode_FromString(self->face->family_name); if (!strcmp(name, "style")) return PyUnicode_FromString(self->face->style_name); if (!strcmp(name, "ascent")) return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); if (!strcmp(name, "descent")) return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); if (!strcmp(name, "num_glyphs")) return PyInt_FromLong(self->face->num_glyphs); PyErr_SetString(PyExc_AttributeError, name); return NULL; } static PyTypeObject py_FT_Font_Type = { PyVarObject_HEAD_INIT(NULL,0) "FT_Font", sizeof(py_FT_FontObject), 0, /* methods */ (destructor)py_FT_font_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)py_FT_font_getattr, /* tp_getattr */ }; #endif /*ifdef RENDERPM_FT*/ static pixBufT* pixBufAlloc(int w, int h, int nchan, gstateColorX bg) { pixBufT* p = PyMem_Malloc(sizeof(pixBufT)); if(p){ size_t n; p->format = 0; /* RGB */ p->buf = PyMem_Malloc(n=w*h*nchan); /* start with white background by default */ if(p->buf){ /*initialise the pixmap pixels*/ art_u8 *b, *lim = p->buf+n; Py_ssize_t stride = w*nchan, i; p->width = w; p->height = h; p->nchan = nchan; p->rowstride = (int)stride; /*set up the background*/ if(bg.stride==0){ /*simple color case*/ art_u32 bgv = (bg.buf[0]<<16) | (bg.buf[1]<<8) | bg.buf[2]; for(i=0;i<(Py_ssize_t)nchan;i++){ art_u8 c= (bgv>>(8*(nchan-i-1)))&0xff; b = p->buf+i; while(bbuf; i = 0; while(bbuf); PyMem_Free(p); *pp = 0; } } typedef double A2DMX[6]; /*Affine transforms*/ typedef struct { art_u32 value; /*the color value*/ int valid; /*if it's valid*/ } gstateColor; typedef struct { PyObject_HEAD A2DMX ctm; gstateColor strokeColor; /*strokeColor*/ double strokeWidth; int lineCap; int lineJoin; double strokeOpacity; gstateColor fillColor; /*fill color*/ int fillMode; int textRenderMode; double fillOpacity; double fontSize; double fontEMSize; /*for scaling to points*/ PyObject *fontNameObj; #ifdef RENDERPM_FT int ft_font; /*if we're trying ft_font things*/ #endif ArtSVP* clipSVP; pixBufT* pixBuf; int pathLen, pathMax; /*current and maximum sizes*/ ArtBpath* path; /*the vector path data*/ ArtVpathDash dash; /*for doing dashes*/ Gt1EncodedFont* font; /*the currently set external font or NULL*/ } gstateObject; static ArtBpath notdefPath[6]={ {ART_MOVETO,0.0,0.0,0.0,0.0,726.0,0.0}, {ART_LINETO,0.0,0.0,0.0,0.0,726.0,692.0}, {ART_LINETO,0.0,0.0,0.0,0.0,35.0,692.0}, {ART_LINETO,0.0,0.0,0.0,0.0,35.0,0.0}, {ART_LINETO,0.0,0.0,0.0,0.0,726.0,0.0}, {ART_END,0.0,0.0,0.0,0.0,0.0,0.0}, }; #ifdef ROBIN_DEBUG #define GFMT "%.17g" #define PATHCODENAME(c) (c==ART_MOVETO_OPEN?"MOVETO":(c==ART_MOVETO?"MOVETO_C":(c==ART_LINETO?"LINETO":(c==ART_CURVETO?"CURVETO":(c==ART_END?"END":"????"))))) static void dump_path(gstateObject* self) { ArtBpath *q = self->path; size_t i; printf("strokeColor=%8.8xX%s strokeWidth=%g fillColor=%8.8xX%s\n", self->strokeColor.value, self->strokeColor.valid ? "valid":"invalid", self->strokeWidth, self->fillColor.value, self->fillColor.valid ? "valid":"invalid" ); printf("ctm: " GFMT " " GFMT " " GFMT " " GFMT " " GFMT " " GFMT " det: " GFMT "\n", self->ctm[0], self->ctm[1], self->ctm[2], self->ctm[3], self->ctm[4], self->ctm[5], self->ctm[0]*self->ctm[3] - self->ctm[1]*self->ctm[2]); printf("path: pathLen=%d pathMax=%d\n",self->pathLen, self->pathMax); for(i=0;i<(size_t)self->pathLen;i++){ char *s; printf("%3d: %-8s, (" GFMT "," GFMT "), (" GFMT "," GFMT "), (" GFMT "," GFMT ")\n", i, s=PATHCODENAME(q->code), q->x1,q->y1,q->x2,q->y2,q->x3,q->y3); if((q++)->code==ART_END || s[0]=='?') break; } fflush(stdout); } void dump_vpath(char* msg, ArtVpath* q) { size_t i; printf("\n%s vpath:\n",msg); for(i=0;i<10000;i++){ char *s; printf("%3d: %-8s, (" GFMT "," GFMT ")\n", i, s=PATHCODENAME(q->code), q->x,q->y); if((q++)->code==ART_END || s[0]=='?') break; } fflush(stdout); } void dump_svp(char* msg, ArtSVP* svp) { int i, j; printf("\n%s svp:\n",msg); for(i=0;in_segs;i++){ ArtSVPSeg *s=svp->segs+i; printf("seg%3d: n_points=%d dir=%s box=(" GFMT "," GFMT ") (" GFMT "," GFMT ")\n", i, s->n_points, s->dir?"dn":"up",s->bbox.x0,s->bbox.y0,s->bbox.x1,s->bbox.y1); for(j=0;jn_points;j++) printf(" (" GFMT "," GFMT ")\n",s->points[j].x, s->points[j].y); } fflush(stdout); } #else #define dump_path(p) #define dump_vpath(m,p) #define dump_svp(m,p) #endif static void bpath_add_point(ArtBpath** pp, int* pn, int *pm, int code, double x[3], double y[3]) { int i = (*pn)++; if(i == *pm) art_expand(*pp, ArtBpath, *pm); (*pp)[i].code = code; (*pp)[i].x1 = x[0]; (*pp)[i].y1 = y[0]; (*pp)[i].x2 = x[1]; (*pp)[i].y2 = y[1]; (*pp)[i].x3 = x[2]; (*pp)[i].y3 = y[2]; } static PyObject* _gstate_bpath_add(int c, char* fmt, gstateObject* self, PyObject* args) { double x[3], y[3]; if(!PyArg_ParseTuple(args,fmt,x+2,y+2)) return NULL; x[0] = x[1] = y[0] = y[1] = 0; bpath_add_point(&(self->path), &(self->pathLen), &(self->pathMax), c, x, y); Py_INCREF(Py_None); return Py_None; } static void gstate_pathEnd(gstateObject* self) { double x[3]; x[0] = x[1] = x[2] = 0; bpath_add_point(&(self->path), &(self->pathLen), &(self->pathMax), ART_END, x, x); self->pathLen--; } static PyObject* gstate_moveTo(gstateObject* self, PyObject* args) { return _gstate_bpath_add(ART_MOVETO_OPEN,"dd:moveTo",self,args); } static PyObject* gstate_moveToClosed(gstateObject* self, PyObject* args) { return _gstate_bpath_add(ART_MOVETO,"dd:moveToClosed",self,args); } static gstateObject* _gstate_pathLenCheck(gstateObject* self) { if(!self->pathLen){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM._gstate_pathLenCheck: path must begin with a moveTo"); return NULL; } return self; } static PyObject* gstate_lineTo(gstateObject* self, PyObject* args) { if(!_gstate_pathLenCheck(self)) return NULL; return _gstate_bpath_add(ART_LINETO,"dd:lineTo",self,args); } static PyObject* gstate_curveTo(gstateObject* self, PyObject* args) { double x[3], y[3]; if(!_gstate_pathLenCheck(self)) return NULL; if(!PyArg_ParseTuple(args,"dddddd:curveTo",x+0,y+0,x+1,y+1,x+2,y+2)) return NULL; bpath_add_point(&(self->path), &(self->pathLen), &(self->pathMax), ART_CURVETO, x, y); Py_INCREF(Py_None); return Py_None; } static PyObject* gstate_pathBegin(gstateObject* self, PyObject* args) { if(!PyArg_ParseTuple(args,":pathBegin")) return NULL; self->pathLen = 0; Py_INCREF(Py_None); return Py_None; } static double _norm1diff(ArtBpath *p, ArtBpath *q) { double rx = p->x3-q->x3; double ry = p->y3-q->y3; if(rx<0) rx = -rx; if(ry<0) ry = -ry; if(rx<=ry) return ry; return rx; } static PyObject* gstate_pathClose(gstateObject* self, PyObject* args) { int c; ArtBpath *p, *q, *q0; double x[3], y[3]; if(!PyArg_ParseTuple(args,":pathClose")) return NULL; p = self->path; for(q0 = q = p + self->pathLen-1;q>=p;q--){ c = q->code; if(c==ART_MOVETO_OPEN){ q->code = ART_MOVETO; /*this closes it*/ if(_norm1diff(q,q0)>1e-8){ x[0] = x[1] = y[0] = y[1] = 0.0; x[2] = q->x3; y[2] = q->y3; bpath_add_point(&self->path,&self->pathLen,&self->pathMax,ART_LINETO,x,y); } break; } else if(c==ART_MOVETO){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_pathClose: path already closed"); return NULL; } } if(qclipSVP){ art_svp_free(self->clipSVP); self->clipSVP = NULL; } Py_INCREF(Py_None); return Py_None; } static art_u32 _RGBA(art_u32 rgb, double alpha) { art_u32 tmp = ((int)(0xFF * alpha))&0xFF; return (rgb << 8) | tmp; } static void _vpath_segment_reverse(ArtVpath *p, ArtVpath *q) { if(pcode; p->code = q->code; q->code = c; } } static void _vpath_reverse(ArtVpath *p) { ArtVpath *q = p; while(q->code!=ART_END){ while((++p)->code==ART_LINETO); _vpath_segment_reverse( q, p-1 ); q = p; } } static double _vpath_segment_area(ArtVpath *p, ArtVpath *q) { double a=0.0, x0,y0, x1,y1; if(p->code==ART_MOVETO){ ArtVpath* p0 = p; while(px; y0 = (p++)->y; if(p==q){ x1 = p0->x; y1 = p0->y; } else{ x1 = p->x; y1 = p->y; } a += x1*y0 - x0*y1; } } return a; } static double _vpath_area(ArtVpath *p) { double a=0.0, t; ArtVpath *q = p, *p0=p; while(q->code!=ART_END){ while((++p)->code==ART_LINETO); t = _vpath_segment_area( q, p); #ifdef ROBIN_DEBUG printf(" closed segment area=%g\n", t ); #endif a += t; q = p; } if(a<=-1e-8) _vpath_reverse( p0 ); return a; } static void _gstate_clipPathSetOrAdd(gstateObject* self, int fillMode, int add, int endIt){ ArtVpath *vpath; ArtVpath *trVpath; if (fillMode==FILL_UNSET) fillMode = self->fillMode; if(endIt) gstate_pathEnd(self); dump_path(self); vpath = art_bez_path_to_vec(self->path, VECSP); dump_vpath("after -->vec",vpath); trVpath = art_vpath_affine_transform (vpath, self->ctm); _vpath_area(trVpath); if (add) { ArtSVP *newSVP = art_svp_from_vpath(trVpath); if(self->clipSVP) { ArtSVP *oldSVP = self->clipSVP; self->clipSVP = art_svp_union(oldSVP,newSVP); art_svp_free(oldSVP); art_svp_free(newSVP); } else{ self->clipSVP = newSVP; } } else{ if(self->clipSVP) art_svp_free(self->clipSVP); self->clipSVP = art_svp_from_vpath(trVpath); } art_free(trVpath); art_free(vpath); } static PyObject* gstate_clipPathSet(gstateObject* self, PyObject* args) { int fillMode = FILL_UNSET; if(!PyArg_ParseTuple(args,"|i:clipPathSet",&fillMode)) return NULL; _gstate_clipPathSetOrAdd(self, fillMode, 0, 1); /*fill add endIt*/ Py_INCREF(Py_None); return Py_None; } static PyObject* gstate_clipPathAdd(gstateObject* self, PyObject* args) { int fillMode = FILL_UNSET; if(!PyArg_ParseTuple(args,"|i:clipPathAdd",&fillMode)) return NULL; _gstate_clipPathSetOrAdd(self, fillMode, 1, 1) /*fill add endIt*/; Py_INCREF(Py_None); return Py_None; } static void _gstate_pathFill(gstateObject* self,int endIt, int vpReverse, int fillMode) { if(self->fillColor.valid){ ArtVpath *vpath, *trVpath, *tmp_vpath; ArtSVP *svp, *tmp_svp; pixBufT* p; double a; if(endIt) gstate_pathEnd(self); dump_path(self); vpath = art_bez_path_to_vec(self->path, VECSP); if(0 && vpReverse) _vpath_reverse(vpath); trVpath = art_vpath_affine_transform(vpath, self->ctm); a = _vpath_area(trVpath); if(fabs(a)>1e-7){ /*fill only larger things*/ tmp_vpath = art_vpath_perturb(trVpath); art_free(trVpath); /*free the original*/ trVpath = tmp_vpath; svp = art_svp_from_vpath(trVpath); dump_svp("fill svp from vpath",svp); if(fillMode==FILL_EVEN_ODD){ tmp_svp = art_svp_uncross(svp); dump_svp("fill svp uncrossed",tmp_svp); art_svp_free(svp); svp = art_svp_rewind_uncrossed(tmp_svp,fillMode==FILL_EVEN_ODD ? ART_WIND_RULE_ODDEVEN : ART_WIND_RULE_NONZERO); art_svp_free(tmp_svp); } if(self->clipSVP) { tmp_svp = svp; dump_svp("fill clip svp path",self->clipSVP); dump_svp("fill svp orig",svp); svp = art_svp_intersect(tmp_svp, self->clipSVP); art_svp_free(tmp_svp); } #ifdef ROBIN_DEBUG printf("fillColor=0x%8.8x, opacity=" GFMT " -->0x%8.8x\n",self->fillColor.value, self->fillOpacity, _RGBA(self->fillColor.value, self->fillOpacity)); dump_vpath("fill vpath", vpath); dump_vpath("fill trVpath", trVpath); dump_svp("fill svp",svp); #endif p = self->pixBuf; art_rgb_svp_alpha(svp, 0,0, p->width, p->height, _RGBA(self->fillColor.value, self->fillOpacity), p->buf, p->rowstride, NULL); art_svp_free(svp); } art_free(trVpath); art_free(vpath); } } static PyObject* gstate_pathFill(gstateObject* self, PyObject* args) { int fillMode = self->fillMode; if(!PyArg_ParseTuple(args,"|i:pathFill",&fillMode)) return NULL; _gstate_pathFill(self,1,0,fillMode); /*endIt vpReverse(ignored) fillMode*/ Py_INCREF(Py_None); return Py_None; } static void _gstate_pathStroke(gstateObject* self, int endIt) { ArtVpath *vpath=NULL, *trVpath; ArtSVP* svp=NULL; ArtSVP* tmp_svp=NULL; pixBufT* p; if(self->strokeColor.valid && self->strokeWidth>0){ if(endIt) gstate_pathEnd(self); dump_path(self); vpath = art_bez_path_to_vec(self->path, VECSP); if(self->dash.dash){ ArtVpath* tvpath=vpath; vpath = art_vpath_dash(tvpath, &self->dash); art_free(tvpath); } dump_vpath("stroke vpath", vpath); trVpath = art_vpath_affine_transform(vpath, self->ctm); _vpath_area(trVpath); svp = art_svp_vpath_stroke(trVpath, self->lineJoin, self->lineCap, self->strokeWidth, 4, 0.5); art_free(trVpath); if(self->clipSVP){ tmp_svp = svp; dump_svp("stroke clip svp path",self->clipSVP); dump_svp("stroke svp orig",svp); svp = art_svp_intersect(tmp_svp, self->clipSVP); art_svp_free(tmp_svp); } dump_svp("stroke svp",svp); p = self->pixBuf; art_rgb_svp_alpha(svp, 0,0, p->width, p->height, _RGBA(self->strokeColor.value, self->strokeOpacity), p->buf, p->rowstride, NULL); art_svp_free(svp); art_free(vpath); } } static PyObject* gstate_pathStroke(gstateObject* self, PyObject* args) { if(!PyArg_ParseTuple(args,":pathStroke")) return NULL; _gstate_pathStroke(self, 1); /*endIt*/ Py_INCREF(Py_None); return Py_None; } #ifdef RENDERPM_FT typedef struct { ArtBpath *path; int pathLen, pathMax; /*current and maximum sizes*/ } _ft_outliner_user_t; static int _ft_move_to(FT_Vector* to, void* user) { _ft_outliner_user_t *self = (_ft_outliner_user_t*)user; double x[3], y[3]; x[0] = x[1] = y[0] = y[1] = 0; x[2] = to->x; y[2] = to->y; bpath_add_point(&(self->path), &(self->pathLen), &(self->pathMax), ART_MOVETO, x, y); return 0; } static int _ft_line_to(FT_Vector* to, void* user) { _ft_outliner_user_t *self = (_ft_outliner_user_t*)user; double x[3], y[3]; x[0] = x[1] = y[0] = y[1] = 0; x[2] = to->x; y[2] = to->y; bpath_add_point(&(self->path), &(self->pathLen), &(self->pathMax), ART_LINETO, x, y); return 0; } static int _ft_cubic_to( FT_Vector* control1, FT_Vector* control2, FT_Vector* to, void* user ) { _ft_outliner_user_t *self = (_ft_outliner_user_t*)user; double x[3], y[3]; x[0] = control1->x; y[0] = control1->y; x[1] = control2->x; y[1] = control2->y; x[2] = to->x; y[2] = to->y; bpath_add_point(&(self->path), &(self->pathLen), &(self->pathMax), ART_CURVETO, x, y); return 0; } static int _ft_conic_to( FT_Vector* control, FT_Vector* to, void* user ) { _ft_outliner_user_t *self = (_ft_outliner_user_t*)user; int i=self->pathLen - 1; FT_Vector p1, p2; FT_Pos x0=(FT_Pos)self->path[i].x3, y0=(FT_Pos)self->path[i].y3, x1=control->x, y1=control->y; p1.x = x0+((x1-x0)*2)/3; p1.y = y0+((y1-y0)*2)/3; p2.x = x1+(to->x-x1)/3; p2.y = y1+(to->y-y1)/3; return _ft_cubic_to(&p1, &p2, to, user); } static FT_Outline_Funcs _ft_outliner = { (FT_Outline_MoveTo_Func)_ft_move_to, (FT_Outline_LineTo_Func)_ft_line_to, (FT_Outline_ConicTo_Func)_ft_conic_to, (FT_Outline_CubicTo_Func)_ft_cubic_to, 0, 0 }; #include FT_OUTLINE_H static ArtBpath *_ft_get_glyph_outline(FT_Face face, int c, _ft_outliner_user_t *user, double *pw) { int err, idx; if(!(idx=FT_Get_Char_Index(face,c)) || (err=FT_Load_Glyph(face,idx,FT_LOAD_NO_SCALE|FT_LOAD_NO_BITMAP))){ return NULL; } if(face->glyph->format!=FT_GLYPH_FORMAT_OUTLINE){ return NULL; } if((err=FT_Outline_Decompose( &face->glyph->outline, &_ft_outliner, (void*)user))){ return NULL; } else{ double x[3]; x[0] = x[1] = x[2] = 0; bpath_add_point(&(user->path), &(user->pathLen), &(user->pathMax), ART_END, x, x); user->pathLen--; *pw = face->glyph->metrics.horiAdvance; } return user->path; } #endif static PyObject* gstate_drawString(gstateObject* self, PyObject* args) { A2DMX orig, trans = {1,0,0,1,0,0}, scaleMat = {1,0,0,1,0,0}; double scaleFactor, x, y, w; char* text; int c, i, textRenderMode=self->textRenderMode; Py_ssize_t textlen; ArtBpath *saved_path, *path; void *font = self->font; PyObject *textObj, *obj0; #ifdef RENDERPM_FT int ft_font = self->ft_font; Py_UCS4 *utext=NULL; _ft_outliner_user_t _ft_data; #endif if(!font){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_drawString: No font set!"); return NULL; } if(!PyArg_ParseTuple(args,"ddO:drawString", &x, &y, &textObj)) return NULL; #ifdef RENDERPM_FT if(ft_font){ /*we need unicode*/ if(PyUnicode_Check(textObj)){ obj0 = textObj; } else if(PyBytes_Check(textObj)){ text = PyBytes_AsString(textObj); textlen = RLPyBytes_Size(textObj); obj0 = PyUnicode_DecodeUTF8(text, textlen,NULL); if(!obj0) return NULL; } else goto L0; textlen = RLPyUnicode_GetLength(obj0); utext = PyUnicode_AsUCS4Copy(obj0); /*we own this as of now*/ if(!utext){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_drawString: Cannot allocate UCS4 memory!"); if(textObj!=obj0) Py_DECREF(obj0); return NULL; } _ft_data.pathMax = 0; _ft_data.path = NULL; } else { #endif /*we need bytes*/ if(PyUnicode_Check(textObj)){ obj0 = PyUnicode_AsUTF8String(textObj); if(!obj0) return NULL; } else if(PyBytes_Check(textObj)){ obj0 = textObj; } else goto L0; text = PyBytes_AsString(obj0); textlen = RLPyBytes_Size(obj0); #ifdef RENDERPM_FT } #endif /*save ctm*/ memcpy(orig, self->ctm, sizeof(A2DMX)); saved_path = self->path; /* translate to x, y */ trans[4] = x; trans[5] = y; art_affine_multiply(self->ctm,trans,self->ctm); scaleFactor = self->fontSize/self->fontEMSize; /*apply font scaling*/ scaleMat[0] = scaleFactor; #ifdef FLIPY scaleMat[3] = -scaleFactor; #else scaleMat[3] = scaleFactor; #endif art_affine_multiply(self->ctm, scaleMat, self->ctm); /*here we render each character one by one, lacks efficiency once again*/ trans[5] = 0; for(i=0;ipath = path; if (textRenderMode==0 || textRenderMode==2 || textRenderMode==4 || textRenderMode==6) _gstate_pathFill(self,0,1,FILL_NON_ZERO) /*endit vpReverse(ignored) fillMode*/; if (textRenderMode==1 || textRenderMode==2 || textRenderMode==5 || textRenderMode==6) _gstate_pathStroke(self,0); /*endIt*/ if (textRenderMode>=4) _gstate_clipPathSetOrAdd(self, -1, 1, 0); /*fill add endIt*/ #ifdef RENDERPM_FT if(!ft_font && path!=notdefPath) #endif art_free(path); } else { w = 761; } /*move to right, scaling width by xscale and don't allow rotations or skew in CTM */ trans[4] = w; /*units are em units right?*/ art_affine_multiply(self->ctm, trans, self->ctm); } if(textObj!=obj0) Py_DECREF(obj0); #ifdef RENDERPM_FT if(utext) PyMem_Free(utext); if(ft_font) art_free(_ft_data.path); #endif /*restore original ctm*/ memcpy(self->ctm, orig, sizeof(A2DMX)); self->path = saved_path; Py_INCREF(Py_None); return Py_None; L0: PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_drawString: text must be bytes/unicode!"); return NULL; } static PyObject* _fmtPathElement(ArtBpath *p, char* name, int n) { PyObject *P = PyTuple_New(n+1); RLPyTuple_SetItem(P, 0, PyUnicode_FromString(name)); if(n==6){ RLPyTuple_SetItem(P, 1, PyFloat_FromDouble(p->x1)); RLPyTuple_SetItem(P, 2, PyFloat_FromDouble(p->y1)); RLPyTuple_SetItem(P, 3, PyFloat_FromDouble(p->x2)); RLPyTuple_SetItem(P, 4, PyFloat_FromDouble(p->y2)); RLPyTuple_SetItem(P, 5, PyFloat_FromDouble(p->x3)); RLPyTuple_SetItem(P, 6, PyFloat_FromDouble(p->y3)); } else { RLPyTuple_SetItem(P, 1, PyFloat_FromDouble(p->x3)); RLPyTuple_SetItem(P, 2, PyFloat_FromDouble(p->y3)); } return P; } static PyObject* _fmtVPathElement(ArtVpath *p, char* name, int n) { PyObject *P = PyTuple_New(n+1); RLPyTuple_SetItem(P, 0, PyUnicode_FromString(name)); RLPyTuple_SetItem(P, 1, PyFloat_FromDouble(p->x)); RLPyTuple_SetItem(P, 2, PyFloat_FromDouble(p->y)); return P; } static PyObject* _get_gstatePath(int n, ArtBpath* path) { PyObject *P = PyTuple_New(n); PyObject *e = NULL; ArtBpath *p; int i; for(i=0;icode){ case ART_MOVETO_OPEN: e = _fmtPathElement(p,"moveTo",2); break; case ART_MOVETO: e = _fmtPathElement(p,"moveToClosed",2); break; case ART_LINETO: e = _fmtPathElement(p,"lineTo",2); break; case ART_CURVETO: e = _fmtPathElement(p,"curveTo",6); break; /*should never be seen*/ case ART_END: break; } RLPyTuple_SetItem(P, i, e); } return P; } static PyObject* _get_gstateVPath(gstateObject *self) { PyObject *e = NULL, *P; ArtVpath *vpath, *v; int i; gstate_pathEnd(self); v = vpath = art_bez_path_to_vec(self->path, VECSP); while(v->code!=ART_END) v++; P = PyTuple_New(v-vpath); i = 0; v =vpath; while(v->code!=ART_END){ switch(v->code){ case ART_MOVETO_OPEN: e = _fmtVPathElement(v,"moveTo",2); break; case ART_MOVETO: e = _fmtVPathElement(v,"moveToClosed",2); break; case ART_LINETO: e = _fmtVPathElement(v,"lineTo",2); break; /*these should never be seen*/ case ART_CURVETO: case ART_END: break; } RLPyTuple_SetItem(P, i, e); v++; i++; } art_free(vpath); return P; } static PyObject* gstate__stringPath(gstateObject* self, PyObject* args) { double w, x=0, y=0, s; char* text; PyObject *P, *p; ArtBpath *path, *pp; Py_ssize_t textlen; int i, c; void *font = self->font; PyObject *textObj, *obj0; #ifdef RENDERPM_FT int ft_font = self->ft_font; Py_UCS4 *utext=NULL; _ft_outliner_user_t _ft_data; #endif if(!font){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate__stringPath: No font set!"); return NULL; } if(!PyArg_ParseTuple(args,"O|dd:_stringPath", &textObj, &x, &y)) return NULL; #ifdef RENDERPM_FT if(ft_font){ /*we need unicode*/ if(PyUnicode_Check(textObj)){ obj0 = textObj; } else if(PyBytes_Check(textObj)){ text = PyBytes_AsString(textObj); textlen = RLPyBytes_Size(textObj); obj0 = PyUnicode_DecodeUTF8(text, textlen,NULL); if(!obj0) return NULL; } else goto L0; textlen = RLPyUnicode_GetLength(obj0); utext = PyUnicode_AsUCS4Copy(obj0); /*we own this as of now*/ if(!utext){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate__stringPath: Cannot allocate UCS4 memory!"); if(textObj!=obj0) Py_DECREF(obj0); return NULL; } _ft_data.pathMax = 0; _ft_data.path = NULL; } else { #endif /*we need bytes*/ if(PyUnicode_Check(textObj)){ obj0 = PyUnicode_AsUTF8String(textObj); if(!obj0) return NULL; } else if(PyBytes_Check(textObj)){ obj0 = textObj; } else goto L0; text = PyBytes_AsString(obj0); textlen = RLPyBytes_Size(obj0); #ifdef RENDERPM_FT } #endif s = self->fontSize/self->fontEMSize; P = PyTuple_New(textlen); for(i=0;icode!=ART_END){ if(pp->code==ART_CURVETO){ pp->x1= pp->x1*s+x; pp->y1= pp->y1*s+y; pp->x2= pp->x2*s+x; pp->y2= pp->y2*s+y; } pp->x3 = pp->x3*s+x; pp->y3 = pp->y3*s+y; pp++; } p = _get_gstatePath((int)(pp-path),path); #ifdef RENDERPM_FT if(!ft_font && path!=notdefPath) #endif art_free(path); } else { /*fprintf(stderr, "No glyph outline for code %d!\n", c);*/ w = 1000; Py_INCREF(Py_None); p = Py_None; } RLPyTuple_SetItem(P, i, p); x += w*s; } if(textObj!=obj0) Py_DECREF(obj0); #ifdef RENDERPM_FT if(utext) PyMem_Free(utext); if(ft_font) art_free(_ft_data.path); #endif return P; L0: PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate__stringPath: text must be bytes/unicode!"); return NULL; } static PyObject* gstate_setFont(gstateObject* self, PyObject* args) { char *fontName; Gt1EncodedFont* f; double fontSize, fontEMSize; int ft_font; PyObject *fontNameObj,*b=NULL; if(!PyArg_ParseTuple(args,"Od:setFont", &fontNameObj, &fontSize)) return NULL; if(PyUnicode_Check(fontNameObj)){ b=PyUnicode_AsUTF8String(fontNameObj); if(!b){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_setFont: bytes conversion of fontName failed"); goto err; } fontName = PyBytes_AsString(b); } else{ fontName = PyBytes_AsString(fontNameObj); } if(!fontName){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_setFont: Invalid fontName"); goto err; } if(fontSize<0){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_setFont: Invalid fontSize"); goto err; } f=gt1_get_encoded_font(fontName); if(f){ fontEMSize = 1000.; ft_font = 0; } #ifdef RENDERPM_FT else{ f = (Gt1EncodedFont*)_ft_get_face(fontName); fontEMSize = f ? ((FT_Face)f)->units_per_EM : 0; ft_font = 1; } #endif if(f){ Py_XDECREF(b); self->font = f; self->fontSize = fontSize; if(self->fontNameObj) Py_DECREF(self->fontNameObj); self->fontNameObj = fontNameObj; Py_INCREF(fontNameObj); self->fontEMSize = fontEMSize; #ifdef RENDERPM_FT self->ft_font = ft_font; #endif Py_INCREF(Py_None); return Py_None; } PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate_setFont: Can't find font!"); err: Py_XDECREF(b); return NULL; } static void _dashFree(gstateObject* self) { if(self->dash.dash){ art_free(self->dash.dash); self->dash.dash = NULL; } } static PyObject* _getA2DMX(double* ctm) { return Py_BuildValue("(dddddd)",ctm[0],ctm[1],ctm[2],ctm[3],ctm[4],ctm[5]); } static int _setA2DMX(PyObject* value, double* ctm) { int i; A2DMX m; if(value==Py_None){ ctm[0] = ctm[3] = 1; ctm[1] = ctm[2] = ctm[4] = ctm[5] = 0; return 1; } if(!(i=PyArg_Parse(value,"(dddddd)",m+0,m+1,m+2,m+3,m+4,m+5))){ PyErr_Clear(); i=PyArg_Parse(value,"[dddddd]",m+0,m+1,m+2,m+3,m+4,m+5); } if(i){ ctm[0] = m[0]; ctm[1] = m[1]; ctm[2] = m[2]; ctm[3] = m[3]; ctm[4] = m[4]; ctm[5] = m[5]; } return i; } #if 0 static void _reverse_rows_inplace( char *buf, int nrows, int stride) { char *rbuf=buf+(nrows-1)*stride, tmp, *lim; int stride2 = stride*2; while(bufctm); src.format = ART_PIX_RGB; src.destroy_data = src.destroy = NULL; src.rowstride = src.width*src.n_channels; src.has_alpha = src.n_channels==4; src.bits_per_sample = 8; art_rgb_pixbuf_affine(self->pixBuf->buf,0,0,self->pixBuf->width,self->pixBuf->height,self->pixBuf->rowstride, (const ArtPixBuf*)&src,ctm,ART_FILTER_NEAREST,NULL); Py_INCREF(Py_None); return Py_None; } static void _safeDecr(PyObject** p) { if(*p){ Py_DECREF(*p); *p = NULL; } } static int _set_gstateDashArray(PyObject* value, gstateObject* self) { if(value==Py_None){ _dashFree(self); return 1; } else { Py_ssize_t n_dash; int i, r=0; PyObject *v=NULL, *pDash=NULL; double offset, *dash=NULL; if(!PySequence_Check(value) || PySequence_Length(value)!=2){ L0: PyErr_SetString(PyExc_ValueError, "dashArray should be None or (offset,(dashlen,....,dashlen,...))"); if(dash) PyMem_Free(dash); L1: _safeDecr(&v); _safeDecr(&pDash); return r; } v = PySequence_GetItem(value,0); if(!PyArg_Parse(v,"d",&offset)) goto L0; pDash = PySequence_GetItem(value,1); if(!PySequence_Check(pDash) || (n_dash=PySequence_Length(pDash))<1) goto L0; dash = art_new(double,n_dash); for(i=0;idash.n_dash = (int)n_dash; self->dash.offset = offset; self->dash.dash = dash; r = 1; goto L1; } } static PyObject* _get_gstateDashArray(gstateObject* self) { PyObject *r=NULL, *pDash=NULL, *v=NULL; int n_dash, i; double *dash; if(!self->dash.dash){ Py_INCREF(Py_None); return Py_None; } if(!(r=PyTuple_New(2))) goto L0; if(!(pDash=PyTuple_New(n_dash=self->dash.n_dash))) goto L0; if(!(v = PyFloat_FromDouble(self->dash.offset))) goto L0; RLPyTuple_SetItem(r,0,v); RLPyTuple_SetItem(r,1,pDash); for(dash=self->dash.dash,i=0;ivalid = 0; return 1; } if((i = PyArg_Parse(value,"i",&cv))){ L0: c->value = cv; c->valid = 1; return 1; } PyErr_Clear(); if(PyObject_HasAttrString(value,"red") && PyObject_HasAttrString(value,"green") && PyObject_HasAttrString(value,"blue")){ double r, g, b; PyObject *v; v = PyObject_GetAttrString(value,"red"); i = PyArg_Parse(v,"d",&r); Py_DECREF(v); if(!i) goto L1; v = PyObject_GetAttrString(value,"green"); i = PyArg_Parse(v,"d",&g); Py_DECREF(v); if(!i) goto L1; v = PyObject_GetAttrString(value,"blue"); i = PyArg_Parse(v,"d",&b); Py_DECREF(v); if(!i) goto L1; cv = ((((int)(r*255))&0xFF)<<16) | ((((int)(g*255))&0xFF)<<8) | (((int)(b*255))&0xFF); goto L0; } L1: PyErr_SetString(PyExc_ValueError, "bad color value"); return 0; } static int _set_gstateColorX(PyObject* value, gstateColorX* c) { int i; if(PySequence_Check(value)){ Py_ssize_t len; i = PyArg_Parse(value,"(iis#)",&c->width,&c->height,&c->buf,&len); if(i){ /*we assume depth 3*/ if(len!=3*c->width*c->height){ PyErr_SetString(PyExc_ValueError, "bad bg image length"); i = 0; } else{ c->stride = c->width*3; } } } else { gstateColor bg = {0xffffffff,1}; i = _set_gstateColor(value,&bg); if(i){ c->buf[0] = (bg.value>>16)&0xff; c->buf[1] = (bg.value>>8)&0xff; c->buf[2] = bg.value&0xff; } } return i; } static PyObject* _get_gstateColor(gstateColor* c) { if(c->valid) return PyInt_FromLong(c->value); Py_INCREF(Py_None); return Py_None; } static PyObject* _get_gstateFontNameI(gstateObject *self) { Gt1EncodedFont *f=self->font; if(f){ #ifdef RENDERPM_FT int ft_font = self->ft_font; if(ft_font){ FT_Face ft_f = (FT_Face)f; char *name = malloc(strlen(ft_f->family_name)+strlen(ft_f->style_name)+2); PyObject* r; strcpy(name,ft_f->family_name); if(ft_f->style_name){ strcat(name," "); strcat(name,ft_f->style_name); } r = PyUnicode_FromString(name); free(name); return r; } #endif return PyUnicode_FromString(gt1_encoded_font_name(f)); } Py_INCREF(Py_None); return Py_None; } static PyObject* _get_gstateFontName(gstateObject *self) { PyObject* r = self->fontNameObj; if(!r) r = Py_None; Py_INCREF(r); return r; } static struct PyMethodDef gstate_methods[] = { {"clipPathClear", (PyCFunction)gstate_clipPathClear, METH_VARARGS, "clipPathClear()"}, {"clipPathSet", (PyCFunction)gstate_clipPathSet, METH_VARARGS, "clipPathSet([fillMode])"}, {"clipPathAdd", (PyCFunction)gstate_clipPathAdd, METH_VARARGS, "clipPathAdd([fillMode])"}, {"curveTo", (PyCFunction)gstate_curveTo, METH_VARARGS, "curveTo(x1,y1,x2,y2,x3,y3)"}, {"drawString", (PyCFunction)gstate_drawString, METH_VARARGS, "drawString(x,y,text)"}, {"lineTo", (PyCFunction)gstate_lineTo, METH_VARARGS, "lineTo(x,y)"}, {"moveTo", (PyCFunction)gstate_moveTo, METH_VARARGS, "moveTo(x,y)"}, {"moveToClosed", (PyCFunction)gstate_moveToClosed, METH_VARARGS, "moveToClosed(x,y)"}, {"pathBegin", (PyCFunction)gstate_pathBegin, METH_VARARGS, "pathBegin()"}, {"pathClose", (PyCFunction)gstate_pathClose, METH_VARARGS, "pathClose()"}, {"pathFill", (PyCFunction)gstate_pathFill, METH_VARARGS, "pathFill([fillMode])"}, {"pathStroke", (PyCFunction)gstate_pathStroke, METH_VARARGS, "pathStroke()"}, {"setFont", (PyCFunction)gstate_setFont, METH_VARARGS, "setFont(fontName,fontSize)"}, {"_stringPath", (PyCFunction)gstate__stringPath, METH_VARARGS, "_stringPath(text[,x=0,y=0])"}, {"_aapixbuf", (PyCFunction)gstate__aapixbuf, METH_VARARGS, "_aapixbuf(dstX,dstY,dstW,dstH,src,srcW,srcH[,srcD]])"}, {NULL, NULL} /* sentinel */ }; static PyObject* gstate_getattr(gstateObject *self, char *name) { #ifdef ROBIN_DEBUG printf("getattr(%s)\n", name); #endif if(!strcmp(name,"ctm")) return _getA2DMX(self->ctm); else if(!strcmp(name,"strokeColor")) return _get_gstateColor(&self->strokeColor); else if(!strcmp(name,"fillColor")) return _get_gstateColor(&self->fillColor); else if(!strcmp(name,"fillMode")) return PyInt_FromLong(self->fillMode); else if(!strcmp(name,"lineCap")) return PyInt_FromLong(self->lineCap); else if(!strcmp(name,"lineJoin")) return PyInt_FromLong(self->lineJoin); else if(!strcmp(name,"hasClipPath")) return PyInt_FromLong(self->clipSVP!=NULL); else if(!strcmp(name,"strokeWidth")) return PyFloat_FromDouble(self->strokeWidth); else if(!strcmp(name,"strokeOpacity")) return PyFloat_FromDouble(self->strokeOpacity); else if(!strcmp(name,"fillOpacity")) return PyFloat_FromDouble(self->fillOpacity); else if(!strcmp(name,"width")) return PyInt_FromLong(self->pixBuf->width); else if(!strcmp(name,"height")) return PyInt_FromLong(self->pixBuf->height); else if(!strcmp(name,"depth")) return PyInt_FromLong(self->pixBuf->nchan); else if(!strcmp(name,"path")) return _get_gstatePath(self->pathLen,self->path); else if(!strcmp(name,"vpath")) return _get_gstateVPath(self); else if(!strcmp(name,"pathLen")) return PyInt_FromLong(self->pathLen); else if(!strcmp(name,"fontSize")) return PyFloat_FromDouble(self->fontSize); else if(!strcmp(name,"fontName")) return _get_gstateFontName(self); else if(!strcmp(name,"fontNameI")) return _get_gstateFontNameI(self); else if(!strcmp(name,"dashArray")) return _get_gstateDashArray(self); else if(!strcmp(name,"textRenderMode")) return PyInt_FromLong(self->textRenderMode); else if(!strcmp(name,"pixBuf")){ pixBufT* p = self->pixBuf; int nw = p->rowstride; PyObject *v = PyBytes_FromStringAndSize((char *)p->buf, p->height*nw); char *r1 = RLPyBytes_AsString(v); char *r2 = r1 + (p->height-1)*nw; while(r1ctm); else if(!strcmp(name,"strokeColor")) i = _set_gstateColor(value,&self->strokeColor); else if(!strcmp(name,"fillColor")) i = _set_gstateColor(value,&self->fillColor); else if(!strcmp(name,"fillMode")) i = PyArg_Parse(value,"i",&self->fillMode); else if(!strcmp(name,"lineCap")) i = PyArg_Parse(value,"i",&self->lineCap); else if(!strcmp(name,"lineJoin")) i = PyArg_Parse(value,"i",&self->lineJoin); else if(!strcmp(name,"strokeWidth")) i = PyArg_Parse(value,"d",&self->strokeWidth); else if(!strcmp(name,"strokeOpacity")) i = PyArg_Parse(value,"d",&self->strokeOpacity); else if(!strcmp(name,"fillOpacity")) i = PyArg_Parse(value,"d",&self->fillOpacity); else if(!strcmp(name,"dashArray")) i = _set_gstateDashArray(value,self); else if(!strcmp(name,"textRenderMode")) i = PyArg_Parse(value,"i",&self->textRenderMode); else if(!strcmp(name,"fontSize")) { i = PyArg_Parse(value,"i",&tmp); if(!i) { if(tmp<0) { i = -1; PyErr_SetString(PyExc_ValueError, name); } } } else { PyErr_SetString(PyExc_AttributeError, name); i = 0; } if(i && !PyErr_Occurred()) i = 0; else { i = -1; if(!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, name); } return i; } static void gstateFree(gstateObject* self) { pixBufFree(&self->pixBuf); _dashFree(self); if(self->path){ art_free(self->path); } if(self->clipSVP){ art_free(self->clipSVP); } if(self->fontNameObj) Py_DECREF(self->fontNameObj); PyObject_DEL(self); } #define gstate__DOC__ "gstate instance\n\ \n\ gstates have the following methods\n\ clipPathClear() clear clipPath\n\ clipPathSet([fillMode]) move current path into clipPath\n\ clipPathAdd([fillMode]) add current path into clipPath\n\ curveTo(x1,y1,x2,y2,x3,y3) #add a curveTo type segment\n\ drawString(x,y,text)\n\ moveTo(x,y) start a segment\n\ moveToClosed(x,y) start a closed segment\n\ lineTo(x,y) add a line segment\n\ pathBegin() initialise\n\ pathClose() close path with LINETO to preceding MOVETO\n\ pathFill([fillMode]) fill current path\n\ pathStroke() stroke current path\n\ setFont(fontName,fontSize) set the font from the gt1 cache\n\ _stringPath(text) return path dump of text\n\ _aapixbuf(dstX,dstY,dstW,dstH,src,srcW,srcH[,srcD]) composite\n\ srcW by srcY depth srcD(=3) image to dst Rect(dstX,dstY,dstW,dstH)\n\ src is string bytes in rgb order\n\ \n\ gstates have the following attributes\n\ ctm 6vec float transformation matrix\n\ strokeColor 32bit stroke colour\n\ fillColor 32bit fill colour\n\ fillMode int fill rule\n\ textRenderMode int controls filling and stroking (follows pdf values)\n\ lineCap int\n\ lineJoin int\n\ hasClipPath readonly int\n\ strokeWidth float\n\ strokeOpacity float\n\ fillOpacity float\n\ dashArray [floatOffset, [floatdash array]]\n\ fontName string readonly\n\ fontSize float readonly\n\ width int readonly pixBuf width\n\ height int readonly pixBuf height\n\ depth int readonly pixBuf depth\n\ path readonly tuple describing the path\n\ pathLen int readonly number of path segments\n\ pixBuf str readonly the pixBuf\n\ " static PyTypeObject gstateType = { PyVarObject_HEAD_INIT(NULL,0) "gstate", /*tp_name*/ sizeof(gstateObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)gstateFree, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ (getattrfunc)gstate_getattr, /*tp_getattr*/ (setattrfunc)gstate_setattr, /*tp_setattr*/ 0, /*tp_compare in 2.x reserved in 3.x*/ (reprfunc)0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ (hashfunc)0, /*tp_hash*/ (ternaryfunc)0, /*tp_call*/ (reprfunc)0, /*tp_str*/ 0L, 0L, 0L, 0L, gstate__DOC__, 0L, /*tp_traverse call function for all accessible objects*/ 0L, /*tp_clear delete references to contained objects */ 0L, /*tp_richcompare rich comparisons */ 0L, /*tp_weaklistoffset weak reference enabler */ 0L, /*tp_iter*/ 0L, /*tp_iternext*/ 0L, /*tp_methods*/ 0L, /*tp_members*/ 0L, /*tp_getset*/ 0L, /*tp_base*/ 0L, /*tp_dict*/ 0L, /*tp_descr_get*/ 0L, /*tp_descr_set*/ 0L, /*tp_dictoffset*/ 0L, /*tp_init*/ 0L, /*tp_alloc*/ 0L, /*tp_new*/ 0L, /*tp_free Low-level free-memory routine */ 0L, /*tp_is_gc For PyObject_IS_GC */ 0L, /*tp_bases*/ 0L, /*tp_mro method resolution order */ 0L, /*tp_cache*/ 0L, /*tp_subclasses*/ 0L /*tp_weaklist*/ }; static art_u32 bgv = 0xffffffff; static gstateObject* gstate(PyObject* module, PyObject* args, PyObject* keywds) { gstateObject* self=NULL; int w, h, d=3, m=12; char *kwlist[] = {"w","h","depth","bg",NULL}; PyObject *pbg=NULL; gstateColorX bg = {1,1,0,(art_u8*)&bgv}; /*default white background*/ if(!PyArg_ParseTupleAndKeywords(args,keywds,"ii|iO:gstate",kwlist,&w,&h,&d,&pbg)) return NULL; if(pbg){ if(!_set_gstateColorX(pbg,&bg)){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate: invalid value for bg"); return NULL; } } if((self = PyObject_NEW(gstateObject, &gstateType))){ self->pixBuf = pixBufAlloc(w,h,d,bg); self->path = art_new(ArtBpath,m); if(!self->pixBuf){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.gstate: no memory"); gstateFree(self); self = NULL; } else { self->ctm[0] = self->ctm[3] = 1.0; self->ctm[1] = self->ctm[2] = self->ctm[4] = self->ctm[5] = 0.0; self->strokeColor.valid = self->fillColor.valid = 0; self->lineCap = self->lineJoin = 0; self->fillMode = FILL_NON_ZERO; /*our default for renderPM differed from PDF/PS*/ self->textRenderMode = 0; self->strokeOpacity = self->strokeWidth = self->fillOpacity = 1.0; self->pathLen = 0; self->pathMax = m; self->clipSVP = NULL; self->font = NULL; self->fontNameObj = NULL; self->fontSize = 10; self->dash.n_dash = 0; self->dash.dash = NULL; } } return self; } static char* my_pfb_reader(void *data, const char *filename, int *psize) { char *pfb = NULL; PyObject *reader = (PyObject*)data; PyObject *arglist; PyObject *result; arglist = Py_BuildValue("(s)",filename); result = PyObject_CallObject(reader, arglist); Py_DECREF(arglist); if(result){ /*the file should have been read as binary*/ if(PyBytes_Check(result)){ char *pystr = RLPyBytes_AsString(result); int size = (int)RLPyBytes_Size(result); *psize = size; memcpy(pfb=gt1_alloc(size),pystr,size); } Py_DECREF(result); } return pfb; } static PyObject* makeT1Font(PyObject* self, PyObject *args, PyObject *kw) { char *name, *pfbPath, **names; size_t N, i; int ok; PyObject *L, *reader=NULL, *u; char *s, *_notdef = ".notdef"; static char *kwlist[] = {"name", "pfbPath", "encoding", "reader", NULL}; if(!PyArg_ParseTupleAndKeywords(args,kw,"ssO|O:makeT1Font", kwlist, &name, &pfbPath, &L, &reader)) return NULL; if(reader){ if(reader==Py_None) reader=NULL; else if(!PyCallable_Check(reader)){ PyErr_SetString(PyExc_ValueError, "makeT1Font reader should be callable, None or absent"); return NULL; } } if(!PySequence_Check(L)){ PyErr_SetString(PyExc_ValueError, "_rl_renderPM.makeT1Font: names should be a sequence object returning strings"); return NULL; } N = PySequence_Length(L); names = PyMem_Malloc(N*sizeof(*names)); for(i=0;i static PyObject* _py_mtrace(PyObject* self, PyObject* args) { int startStop; if(!PyArg_ParseTuple(args,"i:mtrace",&startStop)) return NULL; if (startStop) { mtrace(); } else { muntrace(); } Py_INCREF(Py_None); return Py_None; } #endif #define HEADER_SIZE 512 #define RUN_THRESH 3 #define MAX_RUN 128 /* 0xff = 2, 0xfe = 3, etc */ #define MAX_COUNT 128 /* 0x00 = 1, 0x01 = 2, etc */ /* Opcodes */ #define PICT_picVersion 0x11 #define PICT_background 0x1b #define PICT_headerOp 0x0C00 #define PICT_clipRgn 0x01 #define PICT_PackBitsRect 0x98 #define PICT_EndOfPicture 0xFF #define PICT_MAXCOLORS 256 typedef unsigned char pixel; typedef struct {pixel *p, *buf;} BYTE_STREAM; void pict_putc(unsigned c, BYTE_STREAM* bs) { *bs->p++ = c; } static void pict_putFill(BYTE_STREAM* fd, int n) { register int i; for (i = 0; i < n; i++) (void) pict_putc(0, fd); } static void pict_putShort(BYTE_STREAM* fd, int i) { (void) pict_putc((i >> 8) & 0xff, fd); (void) pict_putc(i & 0xff, fd); } static void pict_putLong( BYTE_STREAM *fd, long i ) { (void) pict_putc((int)((i >> 24) & 0xff), fd); (void) pict_putc(((int)(i >> 16) & 0xff), fd); (void) pict_putc(((int)(i >> 8) & 0xff), fd); (void) pict_putc((int)(i & 0xff), fd); } static void pict_putRect(BYTE_STREAM* fd, int s0, int s1, int s2, int s3) { pict_putShort(fd, s0); pict_putShort(fd, s1); pict_putShort(fd, s2); pict_putShort(fd, s3); } #define runtochar(c) (257-(c)) #define counttochar(c) ((c)-1) static int pict_putRow(BYTE_STREAM* fd, int row, int cols, pixel* rowpixels, char* packed) { register int i; int packcols, count, run, rep, oc; register pixel *pP; pixel lastp; register char *p; run = count = 0; for (i = cols-1, pP = rowpixels + i, p = packed, lastp = *pP; i >= 0; i--, lastp = *pP, pP--){ if (lastp == *pP) run++; else if (run < RUN_THRESH){ while (run > 0){ *p++ = lastp; run--; count++; if (count == MAX_COUNT){ *p++ = counttochar(MAX_COUNT); count -= MAX_COUNT; } } run = 1; } else{ if (count > 0) *p++ = counttochar(count); count = 0; while (run > 0){ rep = run > MAX_RUN ? MAX_RUN : run; *p++ = lastp; *p++ = runtochar(rep); run -= rep; } run = 1; } } if (run < RUN_THRESH){ while (run > 0){ *p++ = lastp; run--; count++; if (count == MAX_COUNT){ *p++ = counttochar(MAX_COUNT); count -= MAX_COUNT; } } } else{ if (count > 0) *p++ = counttochar(count); count = 0; while (run > 0){ rep = run > MAX_RUN ? MAX_RUN : run; *p++ = lastp; *p++ = runtochar(rep); run -= rep; } run = 1; } if (count > 0) *p++ = counttochar(count); packcols = (int)(p - packed); /* how many did we write? */ if (cols >= 250){/*bytesperline>=250*/ pict_putShort(fd, packcols); oc = packcols + 2; } else{ (void) pict_putc(packcols, fd); oc = packcols + 1; } /* now write out the packed row */ while(p != packed){ --p; (void) pict_putc(*p, fd); } return (oc); } static PyObject* pil2pict(PyObject* self, PyObject* args) { PyObject *result; Py_ssize_t npixels, colors; int rows, cols, i, row, oc, tc=-1; size_t len; char *packed; long lpos; pixel *palette, *pixels; BYTE_STREAM OBS; BYTE_STREAM *obs = &OBS; if(!PyArg_ParseTuple(args,"iis#s#|i:pil2pict",&cols,&rows,&pixels,&npixels,&palette,&colors,&tc)) return NULL; colors /= 3; len = HEADER_SIZE*4+colors*4*sizeof(short)+cols*rows*sizeof(pixel); /*generous estimate of maximum size*/ obs->buf = obs->p = (pixel*)malloc(len); /* write the header */ pict_putFill(obs, HEADER_SIZE); /* write picSize and picFrame */ pict_putShort(obs, 0); pict_putRect(obs, 0, 0, rows, cols); /* write version op and version */ pict_putShort(obs, PICT_picVersion); pict_putShort(obs, 0x02FF); pict_putShort(obs, PICT_headerOp); pict_putLong(obs, -1L); pict_putRect(obs, 72, 0, 72, 0); /*h/v resolutions*/ pict_putRect(obs, cols, 0, rows, 0); pict_putFill(obs, 4); /* seems to be needed by many PICT2 programs */ pict_putShort(obs, 0x1e); /*DefHilite*/ pict_putShort(obs, PICT_clipRgn); pict_putShort(obs, 10); pict_putRect(obs, 0, 0, rows, cols); if(tc!=-1){ pict_putShort(obs, PICT_background); pict_putShort(obs, (short)(((unsigned long)((tc>>16)&0xFF)*65535L)/255L)); pict_putShort(obs, (short)(((unsigned long)((tc>>8)&0xFF)*65535L)/255L)); pict_putShort(obs, (short)(((unsigned long)(tc&0xFF)*65535L)/255L)); #if 0 pict_putShort(obs, 0x0f); /*bkcolor*/ pict_putLong(obs, (unsigned long)tc); #endif pict_putShort(obs,5); /*src mode*/ pict_putShort(obs,36|64); pict_putShort(obs,8); /*src mode*/ pict_putShort(obs,36|64); } /* write picture */ pict_putShort(obs, PICT_PackBitsRect); pict_putShort(obs, cols | 0x8000); pict_putRect(obs, 0, 0, rows, cols); pict_putShort(obs, 0); /* pmVersion */ pict_putShort(obs, 0); /* packType */ pict_putLong(obs, 0L); /* packSize */ pict_putRect(obs, 72, 0, 72, 0); /* hRes/vRes */ pict_putShort(obs, 0); /* pixelType */ pict_putShort(obs, 8); /* pixelSize */ pict_putShort(obs, 1); /* cmpCount */ pict_putShort(obs, 8); /* cmpSize */ pict_putLong(obs, 0L); /* planeBytes */ pict_putLong(obs, 0L); /* pmTable */ pict_putLong(obs, 0L); /* pmReserved */ pict_putLong(obs, 0L); /* ctSeed */ pict_putShort(obs, 0); /* ctFlags */ pict_putShort(obs, (int)(colors-1)); /* ctSize */ /*Write out the colormap*/ for (i = 0; i < colors; i++){ pict_putShort(obs, i); pict_putShort(obs, (short)(((unsigned long)palette[3*i]*65535L)/255L)); pict_putShort(obs, (short)(((unsigned long)palette[3*i+1]*65535L)/255L)); pict_putShort(obs, (short)(((unsigned long)palette[3*i+2]*65535L)/255L)); } pict_putRect(obs, 0, 0, rows, cols); /*srcRect*/ pict_putRect(obs, 0, 0, rows, cols); /*dstRect*/ pict_putShort(obs,tc!=-1 ? 36|64 : 0); /*transfer mode*/ /*write out the pixel data.*/ packed = (char*) malloc((unsigned)(cols+cols/MAX_COUNT+1)); oc = 0; for(row=0; rowp-obs->buf; lpos = (int)(obs->p-obs->buf) - HEADER_SIZE; obs->p = obs->buf + HEADER_SIZE; pict_putShort(obs, (short)(lpos & 0xffff)); result = PyBytes_FromStringAndSize((const char *)obs->buf,len); free(obs->buf); return result; } static struct PyMethodDef _methods[] = { {"gstate", (PyCFunction)gstate, METH_VARARGS|METH_KEYWORDS, "gstate(width,height[,depth=3][,bg=0xffffff]) create an\n initialised graphics state"}, {"makeT1Font", (PyCFunction)makeT1Font, METH_VARARGS|METH_KEYWORDS, "makeT1Font(fontName,pfbPath,names)"}, {"delCache", (PyCFunction)delCache, METH_VARARGS, "delCache()"}, {"pil2pict", (PyCFunction)pil2pict, METH_VARARGS, "pil2pict(cols,rows,datastr,palette) return PICT version of im as bytes"}, #ifdef RENDERPM_FT {"ft_get_face", (PyCFunction)ft_get_face, METH_VARARGS|METH_KEYWORDS,"ft_get_face(fontName) --> ft_face instance"}, #ifdef RENDERPM_PARSE_UTF8 {"parse_utf8", (PyCFunction)parse_utf8, METH_VARARGS, "parse_utf8(utf8_string) return UCS list"}, #endif #endif /*ifdef RENDERPM_FT*/ #ifdef MEMORY_DEBUG {"mtrace", (PyCFunction)_py_mtrace, METH_VARARGS|METH_KEYWORDS,"mtrace(startStop) start or stop malloc tracing"}, #endif /*MEMORY_DEBUG*/ {NULL, NULL} /*sentinel*/ }; #ifdef DYNAMIC_ENDIANNESS extern void (*libart_set_DYNAMIC_BIGENDIAN(void)); #endif /*Initialization function for the module*/ static struct PyModuleDef moduleDef = { PyModuleDef_HEAD_INIT, MODULENAME, __DOC__, -1, _methods, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__rl_renderPM(void) { PyObject *m=NULL, *obj=NULL; /*set up the types by hand*/ if(PyType_Ready(&gstateType)<0)goto err; #ifdef RENDERPM_FT if(PyType_Ready(&py_FT_Font_Type)<0)goto err; #endif /* Create the module and add the functions */ m = PyModule_Create(&moduleDef); if(!m)goto err; /* Add some symbolic constants to the module */ obj = PyUnicode_FromString(VERSION); if(!obj)goto err; PyModule_AddObject(m, "_version", obj); obj = PyUnicode_FromString(STRINGIFY(LIBART_VERSION)); if(!obj)goto err; PyModule_AddObject(m, "_libart_version", obj); /*add in the docstring*/ obj = PyUnicode_FromString(__FILE__); if(!obj)goto err; PyModule_AddObject(m, "__file__", obj); #ifdef DYNAMIC_ENDIANNESS libart_set_DYNAMIC_BIGENDIAN(); #endif return m; err: Py_XDECREF(obj); Py_XDECREF(m); return NULL; } rl-renderpm-4.0.3/src/gt1/000077500000000000000000000000001453236046100152335ustar00rootroot00000000000000rl-renderpm-4.0.3/src/gt1/gt1-dict.c000066400000000000000000000035461453236046100170230ustar00rootroot00000000000000/* An implementation of PostScript dict data structures. We use binary trees, because they're so much fun! */ #include "gt1-misc.h" #include "gt1-region.h" #include "gt1-namecontext.h" #include "gt1-value.h" #include "gt1-dict.h" Gt1Dict * gt1_dict_new (Gt1Region *r, int size) { Gt1Dict *dict; if (size < 1) size = 1; dict = (Gt1Dict *)gt1_region_alloc (r, sizeof(Gt1Dict)); dict->n_entries = 0; dict->n_entries_max = size; dict->entries = (Gt1DictEntry *)gt1_region_alloc (r, sizeof (Gt1DictEntry) * size); return dict; } Gt1Value * gt1_dict_lookup (Gt1Dict *dict, Gt1NameId key) { int l, r; int mid; Gt1DictEntry *entries = dict->entries; l = 0; r = dict->n_entries; while (l < r) { mid = (l + r - 1) >> 1; if (entries[mid].key == key) return &entries[mid].val; else if (entries[mid].key > key) r = mid; else l = mid + 1; } return NULL; } void gt1_dict_def (Gt1Region *r, Gt1Dict *d, Gt1NameId key, Gt1Value *val) { int i; int l_ix, r_ix; int mid; Gt1DictEntry *entries = d->entries; l_ix = 0; r_ix = d->n_entries; while (l_ix < r_ix) { mid = (l_ix + r_ix - 1) >> 1; if (entries[mid].key == key) { entries[mid].val = *val; return; } else if (entries[mid].key > key) r_ix = mid; else l_ix = mid + 1; } if (d->n_entries == d->n_entries_max) { int old_size; old_size = d->n_entries_max * sizeof(Gt1DictEntry); d->n_entries_max <<= 1; entries = (Gt1DictEntry *)gt1_region_realloc (r, entries, old_size, d->n_entries_max - 1 * sizeof(Gt1DictEntry)); d->entries = entries; } /* ok, now insert at point l_ix */ for (i = d->n_entries - 1; i >= l_ix; i--) entries[i + 1] = entries[i]; entries[l_ix].key = key; entries[l_ix].val = *val; d->n_entries++; } rl-renderpm-4.0.3/src/gt1/gt1-dict.h000066400000000000000000000015171453236046100170240ustar00rootroot00000000000000/* An implementation of PostScript dict data structures */ /* You must also include gt1-namecontext.h, gt1-region.h, and gt1-value.h. The dicts are all allocated in regions. */ /* For efficiency and type safety, this dict implementation holds values as defined in value.h, i.e. values suitable for a PostScript implementation. If you want to use this dict in other contexts, it would probably be best to duplicate the code. */ typedef struct _Gt1DictEntry Gt1DictEntry; struct _Gt1DictEntry { Gt1NameId key; Gt1Value val; }; /* the dict is sorted by key */ struct _Gt1Dict { int n_entries; int n_entries_max; Gt1DictEntry *entries; }; Gt1Dict *gt1_dict_new (Gt1Region *r, int size); Gt1Value *gt1_dict_lookup (Gt1Dict *dict, Gt1NameId key); void gt1_dict_def (Gt1Region *r, Gt1Dict *d, Gt1NameId key, Gt1Value *val); rl-renderpm-4.0.3/src/gt1/gt1-misc.h000066400000000000000000000021101453236046100170220ustar00rootroot00000000000000/* What allocation functions are we going to use? */ #ifndef __GT1_MISC_H__ #define __GT1_MISC_H__ #include /* for malloc, etc. */ #if defined(macintosh) || defined (__linux__) || defined(__FreeBSD_kernel__) || (__GNU__) # include /* for memcpy() */ #endif #define gt1_alloc malloc #define gt1_free free #define gt1_realloc realloc /* These aren't, strictly speaking, configuration macros, but they're damn handy to have around, and may be worth playing with for debugging. */ #define gt1_new(type, n) ((type *)gt1_alloc ((n) * sizeof(type))) #define gt1_renew(p, type, n) ((type *)gt1_realloc (p, (n) * sizeof(type))) /* This one must be used carefully - in particular, p and max should be variables. They can also be pstruct->el lvalues. */ #define gt1_double(p, type, max) p = gt1_renew (p, type, max <<= 1) typedef int gt1_boolean; #define gt1_false 0 #define gt1_true 1 typedef unsigned char uchar; /* define pi */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* M_PI */ void gt1_die (const char *fmt, ...); #endif /* __GT1_MISC_H__ */ rl-renderpm-4.0.3/src/gt1/gt1-namecontext.c000066400000000000000000000143261453236046100204230ustar00rootroot00000000000000/* A module for a simple "name context", i.e. lisp-style atoms */ #include "gt1-misc.h" #include "gt1-namecontext.h" #if defined(_WIN32) || defined(macintosh) # include #endif /* btw, I do not know who wrote the following comment. I modified this file somewhat from gimp's app/procedural_db.c hash function. */ /* I have seen something like that in Perl/TK. --MW */ static unsigned int gt1_name_context_hash_func (const char *string) { unsigned int result; int c; int i; /* * I tried a zillion different hash functions and asked many other * people for advice. Many people had their own favorite functions, * all different, but no-one had much idea why they were good ones. * I chose the one below (multiply by 9 and add new character) * because of the following reasons: * * 1. Multiplying by 10 is perfect for keys that are decimal strings, * and multiplying by 9 is just about as good. * 2. Times-9 is (shift-left-3) plus (old). This means that each * character's bits hang around in the low-order bits of the * hash value for ever, plus they spread fairly rapidly up to * the high-order bits to fill out the hash value. This seems * works well both for decimal and non-decimal strings. * * tclHash.c -- * * Implementation of in-memory hash tables for Tcl and Tcl-based * applications. * * Copyright (c) 1991-1993 The Regents of the University of California. * Copyright (c) 1994 Sun Microsystems, Inc. */ result = 0; for (i = 0; (c = ((const unsigned char *)string)[i]) != '\0'; i++) result += (result << 3) + c; return result; } static unsigned int gt1_name_context_hash_func_size (const char *string, int size) { unsigned int result; int i; result = 0; for (i = 0; i < size; i++) result += (result << 3) + ((const unsigned char *)string)[i]; return result; } Gt1NameContext * gt1_name_context_new (void) { Gt1NameContext *nc; int i; nc = gt1_new (Gt1NameContext, 1); nc->num_entries = 0; nc->table_size = 16; nc->table = gt1_new (Gt1NameContextHashEntry, nc->table_size); for (i = 0; i < nc->table_size; i++) nc->table[i].name = NULL; return nc; } void gt1_name_context_free (Gt1NameContext *nc) { int i; for (i = 0; i < nc->table_size; i++) if (nc->table[i].name != NULL) gt1_free (nc->table[i].name); gt1_free (nc->table); gt1_free (nc); } static char * gt1_name_context_strdup (const char *s) { int len; char *new; len = strlen (s); new = gt1_new (char, len + 1); memcpy (new, s, len); new[len] = '\0'; return new; } static gt1_boolean gt1_name_context_streq_size (const char *s1, const char *s2, int size2) { int i; /* could rewrite for 32 bits at a time, but I wouldn't worry */ for (i = 0; i < size2; i++) if (s1[i] != s2[i]) return gt1_false; return s1[i] == 0; } static char * gt1_name_context_strdup_size (const char *s, int size) { char *new; new = gt1_new (char, size + 1); memcpy (new, s, size); new[size] = '\0'; return new; } /* double the size of the hash table, rehashing as needed */ static void gt1_name_context_double (Gt1NameContext *nc) { int i, j; int oldsize, newmask; Gt1NameContextHashEntry *old_table, *new_table; oldsize = nc->table_size; old_table = nc->table; nc->table_size = oldsize << 1; newmask = nc->table_size - 1; new_table = gt1_new (Gt1NameContextHashEntry, nc->table_size); for (j = 0; j < nc->table_size; j++) new_table[j].name = NULL; for (i = 0; i < oldsize; i++) { if (old_table[i].name) { for (j = gt1_name_context_hash_func(old_table[i].name); new_table[j & newmask].name; j++); new_table[j & newmask] = old_table[i]; } } gt1_free (old_table); nc->table = new_table; } /* Return the unique (to this name context) Gt1NameId for the given string, allocating a new one if necessary. */ Gt1NameId gt1_name_context_intern (Gt1NameContext *nc, const char *name) { int i; int mask; mask = nc->table_size - 1; for (i = gt1_name_context_hash_func (name); nc->table[i & mask].name; i++) if (!strcmp (nc->table[i & mask].name, name)) return nc->table[i & mask].Gt1NameId; /* not found, allocate a new one */ if (nc->num_entries >= nc->table_size >> 1) { gt1_name_context_double (nc); mask = nc->table_size - 1; for (i = gt1_name_context_hash_func (name); nc->table[i & mask].name; i++); } i &= mask; nc->table[i].name = gt1_name_context_strdup (name); nc->table[i].Gt1NameId = nc->num_entries; return nc->num_entries++; } /* Return the unique (to this name context) Gt1NameId for the given string, return GT1_UNKNOWN if not found*/ Gt1NameId gt1_name_context_interned (Gt1NameContext *nc, const char *name) { int i; int mask; mask = nc->table_size - 1; for (i = gt1_name_context_hash_func (name); nc->table[i & mask].name; i++) if (!strcmp (nc->table[i & mask].name, name)) return nc->table[i & mask].Gt1NameId; return GT1_UNKNOWN; } /* Return the unique (to this name context) Gt1NameId for the given string, allocating a new one if necessary. The string is not necessarily null-terminated; the size is given explicitly. */ Gt1NameId gt1_name_context_intern_size (Gt1NameContext *nc, const char *name, int size) { int i; int mask; mask = nc->table_size - 1; for (i = gt1_name_context_hash_func_size (name, size); nc->table[i & mask].name; i++) if (gt1_name_context_streq_size (nc->table[i & mask].name, name, size)) return nc->table[i & mask].Gt1NameId; /* not found, allocate a new one */ if (nc->num_entries >= nc->table_size >> 1) { gt1_name_context_double (nc); mask = nc->table_size - 1; for (i = gt1_name_context_hash_func_size (name, size); nc->table[i & mask].name; i++); } i &= mask; nc->table[i].name = gt1_name_context_strdup_size (name, size); nc->table[i].Gt1NameId = nc->num_entries; return nc->num_entries++; } /* This one is slow - it's intended for debugging only */ char * gt1_name_context_string (Gt1NameContext *nc, Gt1NameId Gt1NameId) { int j; for (j = 0; j < nc->table_size; j++) if (nc->table[j].name && nc->table[j].Gt1NameId == Gt1NameId) return nc->table[j].name; return NULL; } rl-renderpm-4.0.3/src/gt1/gt1-namecontext.h000066400000000000000000000014341453236046100204240ustar00rootroot00000000000000#ifndef _GT1_NAMECONTEXT_H #define _GT1_NAMECONTEXT_H typedef struct _Gt1NameContext Gt1NameContext; typedef struct _Gt1NameContextHashEntry Gt1NameContextHashEntry; #define GT1_UNKNOWN -1 typedef int Gt1NameId; struct _Gt1NameContextHashEntry { char *name; int Gt1NameId; }; struct _Gt1NameContext { int num_entries; int table_size; Gt1NameContextHashEntry *table; }; Gt1NameContext *gt1_name_context_new (void); void gt1_name_context_free (Gt1NameContext *nc); Gt1NameId gt1_name_context_intern (Gt1NameContext *nc, const char *name); Gt1NameId gt1_name_context_interned (Gt1NameContext *nc, const char *name); Gt1NameId gt1_name_context_intern_size (Gt1NameContext *nc, const char *name, int size); char *gt1_name_context_string (Gt1NameContext *nc, Gt1NameId Gt1NameId); #endif rl-renderpm-4.0.3/src/gt1/gt1-parset1.c000066400000000000000000002062561453236046100174620ustar00rootroot00000000000000#include #include #include #include #if defined(macintosh) # include # define strdup _strdup #endif #include "libart_lgpl/art_bpath.h" #include "gt1-misc.h" #include "gt1-region.h" #include "gt1-namecontext.h" #include "gt1-value.h" #include "gt1-dict.h" #include "gt1-parset1.h" #ifdef AFM # include "parseAFM.h" #endif /* a big-assed module to parse Adobe Type 1 fonts into meaningful info */ #define noVERBOSE static int read_int32_lsb (const char *p) { const unsigned char *q = (unsigned char *)p; return q[0] + (q[1] << 8) + (q[2] << 16) + (q[3] << 24); } /* this is a pfb to pfa converter Reference: Adobe technical note 5040, "Supporting Downloadable PostScript Language Fonts", page 9 */ static char * pfb_to_flat (const char *input, int input_size) { const unsigned char *in = (unsigned char *)input; char *flat; int flat_size, flat_size_max; int in_idx; int length; int i; const char hextab[16] = "0123456789abcdef"; flat_size = 0; flat_size_max = 32768; flat = gt1_new (char, flat_size_max); for (in_idx = 0; in_idx < input_size;) { if (in[in_idx] != 128) { gt1_free (flat); return NULL; } switch (in[in_idx + 1]) { case 1: length = read_int32_lsb (input + in_idx + 2); if (flat_size + length > flat_size_max) { do flat_size_max <<= 1; while (flat_size + length > flat_size_max); flat = gt1_renew (flat, char, flat_size_max); } in_idx += 6; memcpy (flat + flat_size, in + in_idx, length); flat_size += length; in_idx += length; break; case 2: length = read_int32_lsb (input + in_idx + 2); if (flat_size + length * 3 > flat_size_max) { do flat_size_max <<= 1; while (flat_size + length * 3 > flat_size_max); flat = gt1_renew (flat, char, flat_size_max); } in_idx += 6; for (i = 0; i < length; i++) { flat[flat_size++] = hextab[in[in_idx] >> 4]; flat[flat_size++] = hextab[in[in_idx] & 15]; in_idx++; if ((i & 31) == 31 || i == length - 1) flat[flat_size++] = '\n'; } break; case 3: /* zero terminate the returned string */ if (flat_size == flat_size_max) gt1_double (flat, char, flat_size_max); flat[flat_size] = 0; return flat; default: gt1_free (flat); return NULL; } } return flat; } struct _Gt1TokenContext { char *source; int index; int pos; }; typedef enum { TOK_NUM, TOK_STR, TOK_NAME, /* initial / */ TOK_IDENT, TOK_OPENBRACE, TOK_CLOSEBRACE, TOK_END } TokenType; /* we're phasing this out in favor of value.h's Gt1String */ typedef struct _MyGt1String MyGt1String; struct _MyGt1String { char *start; char *fin; }; static void tokenize_free (Gt1TokenContext *tc) { gt1_free (tc->source); gt1_free (tc); } static Gt1TokenContext * tokenize_new (const char *input) { Gt1TokenContext *tc; int length; tc = gt1_new (Gt1TokenContext, 1); length = strlen (input); tc->source = gt1_new (char, length + 1); memcpy (tc->source, input, length + 1); tc->index = 0; tc->pos = 0; return tc; } static Gt1TokenContext * tokenize_new_from_mystring (MyGt1String *input) { Gt1TokenContext *tc; int length; tc = gt1_new (Gt1TokenContext, 1); length = input->fin - input->start; tc->source = gt1_new (char, length + 1); memcpy (tc->source, input->start, length + 1); tc->index = 0; tc->pos = 0; return tc; } /* this returns a TokenType, and sets result to the token contents. Note: this strips delimiters, like the initial /, and the enclosing (). */ static TokenType tokenize_get (Gt1TokenContext *tc, MyGt1String *result) { unsigned char *s = (unsigned char *)tc->source; int index = tc->index; int pos = tc->pos; unsigned char c; TokenType type; /* skip comments altogether (maybe later we want them, though) */ while (c = s[index], isspace (c) || c == '%') { /* skip leading whitespace */ while (isspace (s[index])) { if (s[index] == '\r' || s[index] == '\n') pos = 0; else pos++; index++; } if (s[index] == '%') { do /* skip past end-of-line */ { while (c = s[index], c && c != '\r' && c != '\n') index++; if (s[index] != 0) index++; } while (s[index] == '%'); } } /* skip leading whitespace */ while (c = s[index], isspace (c)) { if (c == '\r' || c == '\n') pos = 0; else pos++; index++; } /* ok, so now we're at the actual start of a token */ result->start = (char*)s + index; c = s[index]; if (c == 0) { result->fin = (char*)s + index; type = TOK_END; } /* note: GhostScript checks much more strenuously. Further, this predicate does not pass the valid number -.9 */ else if (isdigit (c) || c == '.' || (c == '-' && isdigit (s[index + 1]))) { /* numeric token */ while (c = s[index], c && !isspace (c) && c != '{' && c != '/' && c != '[' && c != ']' && c != '}') { index++; pos++; } result->fin = (char*)s + index; type = TOK_NUM; } else if (c == '/') { /* an atom, or whatever that's called */ index++; result->start = (char*)s + index; while (c = s[index], c && !isspace (c) && c != '{' && c != '/' && c != '[' && c != ']' && c != '}' && c != '(') { index++; pos++; } result->fin = (char*)s + index; type = TOK_NAME; } else if (c == '(') { int nest; int backslash; nest = 1; index++; backslash = 0; result->start = (char*)s + index; while (c = s[index], c && nest) { if (backslash) backslash = 0; else if (c == '(') nest++; else if (c == ')') nest--; else if (c == '\\') backslash = 1; index++; if (c == '\r' || c == '\n') pos = 0; else pos++; } /* we could have a c == 0 error case here */ result->fin = (char*)s + index - 1; type = TOK_STR; } else if (c == '{') { index++; result->fin = (char*)s + index; type = TOK_OPENBRACE; } else if (c == '}') { index++; result->fin = (char*)s + index; type = TOK_CLOSEBRACE; } else if (c == '[' || c == ']') { index++; result->fin = (char*)s + index; type = TOK_IDENT; } else { /* treat everything else as an identifier */ while (c = s[index], c && !isspace (c) && c != '{' && c != '/' && c != '[' && c != ']' && c != '}' && c != '(') { index++; pos++; } result->fin = (char*)s + index; if (isspace(c)) index++; /* skip single trailing whitespace char - this is useful for readstring */ type = TOK_IDENT; } tc->index = index; tc->pos = pos; return type; } static int ascii_to_hex (unsigned char c) { if (c <= '9') return c - '0'; else if (c >= 'a') return c + 10 - 'a'; else return c + 10 - 'A'; } /* return a hex byte, or -1 on error */ /* we don't deal with comments here */ static int tokenize_get_hex_byte (Gt1TokenContext *tc) { const unsigned char *s = (const unsigned char *)tc->source; int index = tc->index; int pos = tc->pos; int byte; /* skip leading whitespace */ while (isspace (s[index])) { if (s[index] == '\r' || s[index] == '\n') pos = 0; else pos++; index++; } if (isxdigit (s[index]) && isxdigit (s[index + 1])) { byte = (ascii_to_hex (s[index]) << 4) | ascii_to_hex (s[index + 1]); index += 2; } else byte = -1; tc->index = index; tc->pos = pos; return byte; } /* careful, we're _not_ protected against buffer overruns here */ /* todo: fix this, it's definitely a potential security violation. This almost certainly implies changing the Gt1TokenContext structure to incorporate a size field, and de-emphasizing the use of zero-termination */ static void tokenize_get_raw (Gt1TokenContext *tc, char *buf, int buf_size) { memcpy (buf, tc->source + tc->index, buf_size); tc->index += buf_size; } #ifdef DEBUG static void print_token (TokenType type, MyGt1String *lexeme) { char *start, *fin; start = lexeme->start; fin = lexeme->fin; switch (type) { case TOK_NUM: printf ("number "); break; case TOK_IDENT: printf ("identifier "); break; case TOK_NAME: printf ("name "); break; case TOK_STR: printf ("string "); break; case TOK_OPENBRACE: printf ("open brace "); break; case TOK_CLOSEBRACE: printf ("close brace "); break; case TOK_END: printf ("end "); break; default: break; } while (start != fin) printf ("%c", *start++); printf ("\n"); } static void test_token (const char *flat) { Gt1TokenContext *tc; TokenType type; MyGt1String lexeme; tc = tokenize_new (flat); while (1) { type = tokenize_get (tc, &lexeme); if (type == TOK_END) break; print_token (type, &lexeme); } } #endif /* basic PostScript language types */ typedef struct _Gt1ProcStep { TokenType tok_type; MyGt1String lexeme; } Gt1ProcStep; struct _Gt1Proc { int n_steps; /* sooner or later, we'll want to replace proc steps with plain PostScript values. - probably sooner in fact, because that's the best way to implement nested procedures, which currently do not work. */ Gt1Value steps[1]; }; /* low-level PostScript routines */ /* note: resulting array is _uninitialized_ ! */ static Gt1Array * array_new (Gt1Region *r, int size) { Gt1Array *array; array = (Gt1Array *)gt1_region_alloc (r, sizeof(Gt1Array) + (size - 1) * sizeof(Gt1Value)); array->n_values = size; return array; } struct _Gt1PSContext { Gt1Region *r; /* the region all PS values are allocated into */ Gt1TokenContext *tc; /* this is for readstring, eexec, etc. */ Gt1NameContext *nc; /* the context for all names */ Gt1Value *value_stack; int n_values, n_values_max; /* ghostscript also has an execution stack - what's that? */ Gt1Dict **gt1_dict_stack; int n_dicts, n_dicts_max; /* a special dict that holds all the fonts */ Gt1Dict *fonts; Gt1TokenContext **file_stack; /* invariant: top of file stack == tc */ int n_files, n_files_max; int quit; /* maybe this should be a string, for error messages too */ }; /* a very basic PostScript interpreter */ /* make sure that value stack has enough room for pushing n values. */ static void ensure_stack (Gt1PSContext *psc, int n) { if (psc->n_values + n == psc->n_values_max) { psc->n_values_max <<= 1; psc->value_stack = gt1_renew (psc->value_stack, Gt1Value, psc->n_values_max); } } static double parse_num (MyGt1String *number) { double sign; double mantissa; double decimal; int exp_sign; int exp; int i, length; const unsigned char *start; start = (const unsigned char *)number->start; length = number->fin - number->start; i = 0; sign = 1; if (i < length && start[i] == '-') { sign = -1; i++; } else if (i < length && start[i] == '+') i++; mantissa = 0; while (i < length && isdigit (start[i])) { mantissa = (mantissa * 10) + start[i] - '0'; i++; } if (i < length && start[i] == '.') { i++; decimal = 1; while (i < length && isdigit (start[i])) { decimal *= 0.1; mantissa += (start[i] - '0') * decimal; i++; } } if (i < length && (start[i] == 'e' || start[i] == 'E')) { i++; exp_sign = 1; if (i < length && start[i] == '-') { exp_sign = -1; i++; } else if (i < length && start[i] == '+') i++; exp = 0; while (i < length && isdigit (start[i])) { exp = (exp * 10) + start[i] - '0'; i++; } mantissa *= pow (10, exp * exp_sign); } return sign * mantissa; } #ifdef DEBUG static void print_mystring (MyGt1String *str) { char *start, *fin; start = str->start; fin = str->fin; while (start != fin) printf ("%c", *start++); } #endif static void print_string (Gt1String *str) { char *start; int size; int i; start = str->start; size = str->size; for (i = 0; i < size; i++); printf ("%c", start[i]); } static void print_value (Gt1PSContext *psc, Gt1Value *val) { switch (val->type) { case GT1_VAL_NUM: printf ("%g", val->val.num_val); break; case GT1_VAL_BOOL: printf ("%s", val->val.bool_val ? "true" : "false"); break; case GT1_VAL_STR: printf ("\""); print_string (&val->val.str_val); printf ("\""); break; case GT1_VAL_NAME: printf ("/%s", gt1_name_context_string (psc->nc, val->val.name_val)); break; case GT1_VAL_UNQ_NAME: printf ("%s", gt1_name_context_string (psc->nc, val->val.name_val)); break; case GT1_VAL_DICT: printf ("", val->val.dict_val->n_entries, val->val.dict_val->n_entries_max); break; case GT1_VAL_ARRAY: printf (""); break; case GT1_VAL_PROC: #if 1 printf (""); #else printf ("{ "); { int i; for (i = 0; i < val->val.proc_val->n_values; i++) { print_value (psc, &val->val.proc_val->vals[i]); printf (" "); } printf ("}"); } #endif break; case GT1_VAL_FILE: printf (""); break; case GT1_VAL_INTERNAL: printf (""); case GT1_VAL_MARK: printf (""); break; default: printf ("???%d", val->type); } } #ifdef DEBUG static void print_token_short (TokenType type, MyGt1String *lexeme) { char *start, *fin; start = lexeme->start; fin = lexeme->fin; switch (type) { case TOK_NUM: print_mystring (lexeme); break; case TOK_IDENT: print_mystring (lexeme); break; case TOK_NAME: printf ("/"); print_mystring (lexeme); break; case TOK_STR: printf ("("); print_mystring (lexeme); printf (")"); break; case TOK_OPENBRACE: printf ("{"); break; case TOK_CLOSEBRACE: printf ("}"); break; case TOK_END: printf ("end "); break; default: break; } } #endif static void print_value_deep (Gt1PSContext *psc, Gt1Value *val, int nest) { int i, j; for (i = 0; i < nest; i++) printf (" "); switch (val->type) { case GT1_VAL_NUM: printf ("%g", val->val.num_val); break; case GT1_VAL_BOOL: printf ("%s", val->val.bool_val ? "true" : "false"); break; case GT1_VAL_STR: printf ("\""); print_string (&val->val.str_val); printf ("\""); break; case GT1_VAL_NAME: printf ("/%s", gt1_name_context_string (psc->nc, val->val.name_val)); break; case GT1_VAL_UNQ_NAME: printf ("%s", gt1_name_context_string (psc->nc, val->val.name_val)); break; case GT1_VAL_DICT: printf (" [\n", val->val.dict_val->n_entries, val->val.dict_val->n_entries_max); for (i = 0; i < val->val.dict_val->n_entries; i++) { for (j = 0; j < nest; j++) printf (" "); printf ("key %d\n", val->val.dict_val->entries[i].key); print_value_deep (psc, &val->val.dict_val->entries[i].val, nest + 1); } for (j = 0; j < nest; j++) printf (" "); printf ("]"); break; case GT1_VAL_ARRAY: printf ("[\n"); for (i = 0; i < val->val.array_val->n_values; i++) { print_value_deep (psc, &val->val.array_val->vals[i], nest + 1); } for (j = 0; j < nest; j++) printf (" "); printf ("]"); break; case GT1_VAL_PROC: printf ("{\n"); for (i = 0; i < val->val.proc_val->n_values; i++) { print_value_deep (psc, &val->val.proc_val->vals[i], nest + 1); } for (j = 0; j < nest; j++) printf (" "); printf ("}"); break; case GT1_VAL_FILE: printf (""); break; case GT1_VAL_INTERNAL: printf (""); case GT1_VAL_MARK: printf (""); break; default: printf ("???"); } printf ("\n"); } #ifdef DEBUG static void print_stack (Gt1PSContext *psc) { int i; for (i = 0; i < psc->n_values; i++) { print_value (psc, &psc->value_stack[i]); if (i != psc->n_values - 1) printf (" "); } printf ("\n"); } #endif static void eval_ps_val (Gt1PSContext *psc, Gt1Value *val); static void eval_proc (Gt1PSContext *psc, Gt1Proc *proc) { int i; #ifdef VERBOSE printf ("begin proc evaluation\n"); #endif for (i = 0; !psc->quit && i < proc->n_values; i++) eval_ps_val (psc, &proc->vals[i]); #ifdef VERBOSE printf ("end proc evaluation\n"); #endif } static Gt1Value * gt1_dict_stack_lookup (Gt1PSContext *psc, Gt1NameId key) { int i; Gt1Value *val; for (i = psc->n_dicts - 1; i >= 0; i--) { val = gt1_dict_lookup (psc->gt1_dict_stack[i], key); if (val != NULL) return val; } return NULL; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_number (Gt1PSContext *psc, double *result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_NUM) { printf ("type error - expecting number\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.num_val; return 1; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_dict (Gt1PSContext *psc, Gt1Dict **result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_DICT) { printf ("type error - expecting dict\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.dict_val; return 1; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_name (Gt1PSContext *psc, Gt1NameId *result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_NAME) { printf ("type error - expecting atom\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.name_val; return 1; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_file (Gt1PSContext *psc, Gt1TokenContext **result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_FILE) { printf ("type error - expecting file\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.file_val; return 1; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_string (Gt1PSContext *psc, Gt1String *result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_STR) { printf ("type error - expecting string\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.str_val; return 1; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_array (Gt1PSContext *psc, Gt1Array **result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_ARRAY) { printf ("type error - expecting array\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.array_val; return 1; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_bool (Gt1PSContext *psc, int *result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_BOOL) { printf ("type error - expecting bool\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.bool_val; return 1; } /* return 1 on success, with result set to the top of the value stack */ static int get_stack_proc (Gt1PSContext *psc, Gt1Proc **result, int index) { if (psc->n_values < index) { printf ("stack underflow\n"); psc->quit = 1; return 0; } if (psc->value_stack[psc->n_values - index].type != GT1_VAL_PROC) { printf ("type error - expecting proc\n"); psc->quit = 1; return 0; } *result = psc->value_stack[psc->n_values - index].val.proc_val; return 1; } /* here begin the internal procedures */ static void internal_dict (Gt1PSContext *psc) { Gt1Dict *dict; double d_size; if (get_stack_number (psc, &d_size, 1)) { dict = gt1_dict_new (psc->r, (int)d_size); psc->value_stack[psc->n_values - 1].type = GT1_VAL_DICT; psc->value_stack[psc->n_values - 1].val.dict_val = dict; } } static void internal_begin (Gt1PSContext *psc) { Gt1Dict *dict; if (get_stack_dict (psc, &dict, 1)) { if (psc->n_dicts == psc->n_dicts_max) gt1_double (psc->gt1_dict_stack, Gt1Dict *, psc->n_dicts_max); psc->gt1_dict_stack[psc->n_dicts++] = dict; psc->n_values--; } } static void internal_end (Gt1PSContext *psc) { /* note: this magic constant changes if we separate out the internal dict from the user one; in fact, GhostScript uses three. */ if (psc->n_dicts == 1) { printf ("dict stack underflow\n"); psc->quit = 1; } psc->n_dicts--; } static void internal_dup (Gt1PSContext *psc) { if (psc->n_values == 0) { printf ("stack underflow\n"); psc->quit = 1; } else { ensure_stack (psc, 1); psc->value_stack[psc->n_values] = psc->value_stack[psc->n_values - 1]; psc->n_values++; } } static void internal_pop (Gt1PSContext *psc) { if (psc->n_values == 0) { printf ("stack underflow\n"); psc->quit = 1; } else psc->n_values--; } static void internal_exch (Gt1PSContext *psc) { Gt1Value tmp; int stack_size; stack_size = psc->n_values; if (stack_size < 2) { printf ("stack underflow\n"); psc->quit = 1; } else { tmp = psc->value_stack[stack_size - 2]; psc->value_stack[stack_size - 2] = psc->value_stack[stack_size - 1]; psc->value_stack[stack_size - 1] = tmp; } } /* this doesn't do anything - we don't enforce readonly */ static void internal_readonly (Gt1PSContext *psc) { if (psc->n_values == 0) { printf ("stack underflow\n"); psc->quit = 1; } } /* this doesn't do anything - we don't enforce executeonly */ static void internal_executeonly (Gt1PSContext *psc) { if (psc->n_values == 0) { printf ("stack underflow\n"); psc->quit = 1; } } /* this doesn't do anything - we don't enforce noaccess */ static void internal_noaccess (Gt1PSContext *psc) { if (psc->n_values == 0) { printf ("stack underflow\n"); psc->quit = 1; } } static void internal_def (Gt1PSContext *psc) { Gt1NameId key; Gt1Dict *dict; if (get_stack_name (psc, &key, 2)) { dict = psc->gt1_dict_stack[psc->n_dicts - 1]; gt1_dict_def (psc->r, dict, key, &psc->value_stack[psc->n_values - 1]); psc->n_values -= 2; } } static void internal_false (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_BOOL; psc->value_stack[psc->n_values].val.bool_val = gt1_false; psc->n_values++; } static void internal_true (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_BOOL; psc->value_stack[psc->n_values].val.bool_val = gt1_true; psc->n_values++; } static void internal_StandardEncoding (Gt1PSContext *psc) { ensure_stack (psc, 1); /* todo: push actual encoding array */ psc->value_stack[psc->n_values].type = GT1_VAL_NUM; psc->value_stack[psc->n_values].val.num_val = 42; psc->n_values++; } static void internalop_openbracket (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_MARK; psc->n_values++; } static void internalop_closebracket (Gt1PSContext *psc) { int i; Gt1Array *array; int size, start_idx; for (i = psc->n_values - 1; i >= 0; i--) if (psc->value_stack[i].type == GT1_VAL_MARK) break; if (psc->value_stack[i].type != GT1_VAL_MARK) { printf ("unmatched mark\n"); psc->quit = 1; } start_idx = i + 1; size = psc->n_values - start_idx; array = array_new (psc->r, size); for (i = 0; i < size; i++) array->vals[i] = psc->value_stack[start_idx + i]; psc->n_values -= size; psc->value_stack[psc->n_values - 1].type = GT1_VAL_ARRAY; psc->value_stack[psc->n_values - 1].val.array_val = array; } static void internal_currentdict (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_DICT; psc->value_stack[psc->n_values].val.dict_val = psc->gt1_dict_stack[psc->n_dicts - 1]; psc->n_values++; } static void internal_currentfile (Gt1PSContext *psc) { /* todo: we want to move away from a tc pointer towards tags, to avoid potential security holes from misusing these data structures */ ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_FILE; psc->value_stack[psc->n_values].val.file_val = psc->tc; psc->n_values++; } #define EEXEC_C1 ((unsigned short)52845) #define EEXEC_C2 ((unsigned short)22719) /* return number of bytes in result */ static int decrypt_eexec (char *plaintext, const char *ciphertext, int ciphertext_size) { int i; unsigned short r; unsigned char cipher; unsigned char plain; r = 55665; /* initial key */ for (i = 0; i < ciphertext_size; i++) { cipher = ciphertext[i]; plain = (cipher ^ (r>>8)); r = (cipher + r) * EEXEC_C1 + EEXEC_C2; if (i >= 4) plaintext[i - 4] = plain; } return ciphertext_size - 4; } /* this one is great fun! */ static void internal_eexec (Gt1PSContext *psc) { Gt1TokenContext *file_tc; char *ciphertext; int ciphertext_size, ciphertext_size_max; char *plaintext; int plaintext_size; int num_nulls; int byte; MyGt1String string; Gt1TokenContext *new_tc; if (get_stack_file (psc, &file_tc, 1)) { psc->n_values--; /* first, suck the encrypted stream from the specified file */ ciphertext_size = 0; ciphertext_size_max = 512; ciphertext = gt1_new (char, ciphertext_size_max); num_nulls = 0; while (num_nulls < 16) { if (ciphertext_size == ciphertext_size_max) gt1_double (ciphertext, char, ciphertext_size_max); byte = tokenize_get_hex_byte (file_tc); if (byte < 0) { printf ("eexec input appears to be truncated\n"); psc->quit = 1; return; } if (byte == 0) num_nulls++; else num_nulls = 0; ciphertext[ciphertext_size++] = byte; } /* then, decrypt it */ plaintext = gt1_new (char, ciphertext_size); plaintext_size = decrypt_eexec (plaintext, ciphertext, ciphertext_size); gt1_free (ciphertext); #if 1 && defined(VERBOSE) fwrite (plaintext, 1, plaintext_size, stdout); #endif /* finally, create a new Gt1TokenContext for the string, and switch to executing it. */ string.start = plaintext; string.fin = plaintext + plaintext_size; new_tc = tokenize_new_from_mystring (&string); gt1_free (plaintext); if (psc->n_files_max == psc->n_files) { printf ("overflow of file stack\n"); psc->quit = 1; return; } psc->file_stack[psc->n_files++] = new_tc; psc->tc = new_tc; /* alternatively, we could have recursively called the PostScript evaluation loop from here, but this seems to be just as good. */ } } static void internal_array (Gt1PSContext *psc) { Gt1Array *array; double d_size; if (get_stack_number (psc, &d_size, 1)) { array = array_new (psc->r, (int)d_size); psc->value_stack[psc->n_values - 1].type = GT1_VAL_ARRAY; psc->value_stack[psc->n_values - 1].val.array_val = array; } } static void internal_string (Gt1PSContext *psc) { Gt1String string; double d_size; int size; if (get_stack_number (psc, &d_size, 1)) { size = (int)d_size; string.start = gt1_region_alloc (psc->r, size); string.size = size; memset (string.start, 0, size); psc->value_stack[psc->n_values - 1].type = GT1_VAL_STR; psc->value_stack[psc->n_values - 1].val.str_val = string; } } static void internal_readstring (Gt1PSContext *psc) { Gt1String string; Gt1TokenContext *file_tc; if (get_stack_string (psc, &string, 1) && get_stack_file (psc, &file_tc, 2)) { tokenize_get_raw (file_tc, string.start, string.size); psc->value_stack[psc->n_values - 2].type = GT1_VAL_STR; psc->value_stack[psc->n_values - 2].val.str_val = string; psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL; psc->value_stack[psc->n_values - 1].val.bool_val = gt1_true; } } static void internal_put (Gt1PSContext *psc) { Gt1Array *array; double d_index; int index; Gt1Dict *dict; Gt1NameId key; if (psc->n_values >= 3 && psc->value_stack[psc->n_values - 3].type == GT1_VAL_DICT && get_stack_name (psc, &key, 2)) { /* dict key val put -- */ get_stack_dict (psc, &dict, 3); gt1_dict_def (psc->r, dict, key, &psc->value_stack[psc->n_values - 1]); psc->n_values -= 3; } else if (psc->n_values >= 3 && psc->value_stack[psc->n_values - 3].type == GT1_VAL_PROC && get_stack_number (psc, &d_index, 2)) { array = psc->value_stack[psc->n_values - 3].val.proc_val; index = (int)d_index; if (index < 0 || index >= array->n_values) { printf ("range check\n"); psc->quit = 1; return; } array->vals[index] = psc->value_stack[psc->n_values - 1]; psc->n_values -= 3; } else if (psc->n_values >= 3 && get_stack_array (psc, &array, 3) && get_stack_number (psc, &d_index, 2)) { /* array index val put -- */ index = (int)d_index; if (index < 0 || index >= array->n_values) { printf ("range check\n"); psc->quit = 1; return; } array->vals[index] = psc->value_stack[psc->n_values - 1]; psc->n_values -= 3; } } static void internal_get (Gt1PSContext *psc) { Gt1Array *array; double d_index; int index; Gt1Dict *dict; Gt1NameId key; Gt1Value *val; if (psc->n_values >= 2 && psc->value_stack[psc->n_values - 2].type == GT1_VAL_DICT && get_stack_name (psc, &key, 1)) { /* dict key get val */ get_stack_dict (psc, &dict, 2); val = gt1_dict_lookup (dict, key); if (val == NULL) { printf ("key not found\n"); psc->quit = 1; return; } #ifdef VERBOSE printf ("value: "); print_value (psc, val); printf ("\n"); #endif psc->n_values -= 1; psc->value_stack[psc->n_values - 1] = *val; } else if (psc->n_values >= 2 && psc->value_stack[psc->n_values - 2].type == GT1_VAL_PROC && get_stack_number (psc, &d_index, 1)) { /* array index get val */ array = psc->value_stack[psc->n_values - 2].val.proc_val; index = (int)d_index; if (index < 0 || index >= array->n_values) { printf ("range check\n"); psc->quit = 1; return; } psc->n_values -= 1; psc->value_stack[psc->n_values - 1] = array->vals[index]; } else if (get_stack_array (psc, &array, 2) && get_stack_number (psc, &d_index, 1)) { /* array index get val */ index = (int)d_index; if (index < 0 || index >= array->n_values) { printf ("range check\n"); psc->quit = 1; return; } psc->n_values -= 1; psc->value_stack[psc->n_values - 1] = array->vals[index]; } } static void internal_index (Gt1PSContext *psc) { double d_index; int index; if (get_stack_number (psc, &d_index, 1)) { index = (int)d_index; if (index < 0 || index > psc->n_values - 2) { printf ("index range check\n"); psc->quit = 1; return; } psc->value_stack[psc->n_values - 1] = psc->value_stack[psc->n_values - (index + 2)]; } } static void internal_definefont (Gt1PSContext *psc) { Gt1NameId key; Gt1Dict *dict; if (psc->n_values < 2) { printf ("stack underflow\n"); psc->quit = 1; } else if (get_stack_name (psc, &key, 2)) { dict = psc->fonts; gt1_dict_def (psc->r, dict, key, &psc->value_stack[psc->n_values - 1]); #ifdef VERBOSE print_value_deep (psc, &psc->value_stack[psc->n_values - 1], 0); #endif psc->n_values -= 1; } } static void internal_mark (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_MARK; psc->n_values++; } static void internal_closefile (Gt1PSContext *psc) { Gt1TokenContext *tc; if (get_stack_file (psc, &tc, 1)) { if (psc->n_files == 1) { printf ("file stack underflow\n"); psc->quit = 1; } else if (psc->file_stack[psc->n_files - 1] == tc) { /* pop the file stack */ tokenize_free (psc->tc); psc->n_files--; psc->tc = psc->file_stack[psc->n_files - 1]; psc->n_values--; } else { printf ("closefile: whoa, file cowboy!\n"); psc->quit = 1; } } } static void internal_cleartomark (Gt1PSContext *psc) { int i; for (i = psc->n_values - 1; i >= 0; i--) if (psc->value_stack[i].type == GT1_VAL_MARK) break; if (psc->value_stack[i].type != GT1_VAL_MARK) { printf ("cleartomark: unmatched mark\n"); psc->quit = 1; } psc->n_values = i; } static void internal_systemdict (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_DICT; psc->value_stack[psc->n_values].val.dict_val = psc->gt1_dict_stack[0]; psc->n_values++; } static void internal_userdict (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_DICT; psc->value_stack[psc->n_values].val.dict_val = psc->gt1_dict_stack[2]; psc->n_values++; } static void internal_known (Gt1PSContext *psc) { Gt1NameId key; Gt1Dict *dict; int known; if (psc->n_values >= 2 && get_stack_dict (psc, &dict, 2) && get_stack_name (psc, &key, 1)) { known = (gt1_dict_lookup (dict, key) != 0); psc->n_values -= 1; psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL; psc->value_stack[psc->n_values - 1].val.bool_val = known; } } static void internal_ifelse (Gt1PSContext *psc) { Gt1Proc *proc1, *proc2; int bool; if (psc->n_values >= 3 && get_stack_bool (psc, &bool, 3) && get_stack_proc (psc, &proc1, 2) && get_stack_proc (psc, &proc2, 1)) { psc->n_values -= 3; if (bool) eval_proc (psc, proc1); else eval_proc (psc, proc2); } } static void internal_if (Gt1PSContext *psc) { Gt1Proc *proc; int bool; if (psc->n_values >= 2 && get_stack_bool (psc, &bool, 2) && get_stack_proc (psc, &proc, 1)) { psc->n_values -= 2; if (bool) eval_proc (psc, proc); } } static void internal_for (Gt1PSContext *psc) { double initial, increment, limit; Gt1Proc *proc; double val; if (psc->n_values >= 4 && get_stack_number (psc, &initial, 4) && get_stack_number (psc, &increment, 3) && get_stack_number (psc, &limit, 2) && get_stack_proc (psc, &proc, 1)) { psc->n_values -= 4; for (val = initial; !psc->quit && (increment > 0 ? (val <= limit) : (val >= limit)); val += increment) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_NUM; psc->value_stack[psc->n_values].val.num_val = val; psc->n_values++; eval_proc (psc, proc); } } } static void internal_not (Gt1PSContext *psc) { int bool; if (psc->n_values >= 1 && get_stack_bool (psc, &bool, 1)) { psc->value_stack[psc->n_values - 1].val.bool_val = !bool; } } static void internal_bind (Gt1PSContext *psc) { Gt1Proc *proc; if (psc->n_values >= 1 && get_stack_proc (psc, &proc, 1)) { /* todo: implement, when procs become normal values */ } } static void internal_exec (Gt1PSContext *psc) { Gt1Proc *proc; if (psc->n_values >= 1 && get_stack_proc (psc, &proc, 1)) { psc->n_values -= 1; eval_proc (psc, proc); } } static void internal_count (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_NUM; psc->value_stack[psc->n_values].val.num_val = psc->n_values; psc->n_values++; } static void internal_eq (Gt1PSContext *psc) { double a, b; Gt1NameId na, nb; if (psc->n_values < 2) { printf ("stack underflow\n"); psc->quit = 1; return; } if (psc->value_stack[psc->n_values - 1].type == GT1_VAL_NAME && get_stack_name (psc, &na, 2) && get_stack_name (psc, &nb, 1)) { psc->n_values -= 1; psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL; psc->value_stack[psc->n_values - 1].val.bool_val = (na == nb); } else if (get_stack_number (psc, &a, 2) && get_stack_number (psc, &b, 1)) { psc->n_values -= 1; psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL; psc->value_stack[psc->n_values - 1].val.bool_val = (a == b); } } static void internal_ne (Gt1PSContext *psc) { internal_eq (psc); if (!psc->quit) psc->value_stack[psc->n_values - 1].val.bool_val = !psc->value_stack[psc->n_values - 1].val.bool_val; } static void internal_type (Gt1PSContext *psc) { Gt1ValueType type; if (psc->n_values >= 1) { type = psc->value_stack[psc->n_values - 1].type; if (type == GT1_VAL_NUM) { psc->value_stack[psc->n_values - 1].type = GT1_VAL_NAME; psc->value_stack[psc->n_values - 1].val.name_val = gt1_name_context_intern (psc->nc, "integertype"); } else { printf ("type not fully implemented"); } } } static void internal_cvx (Gt1PSContext *psc) { Gt1Value *val; if (psc->n_values < 1) { printf ("stack underflow\n"); psc->quit = 1; } else { val = &psc->value_stack[psc->n_values - 1]; if (val->type == GT1_VAL_NAME) val->type = GT1_VAL_UNQ_NAME; else if (val->type == GT1_VAL_ARRAY) val->type = GT1_VAL_PROC; else { printf ("warning: cvx called on "); print_value (psc, val); printf ("\n"); } } } static void internal_matrix (Gt1PSContext *psc) { Gt1Array *array; int i; array = array_new (psc->r, 6); for (i = 0; i < 6; i++) { array->vals[i].type = GT1_VAL_NUM; array->vals[i].val.num_val = (i == 0 || i == 3); } psc->value_stack[psc->n_values].type = GT1_VAL_ARRAY; psc->value_stack[psc->n_values].val.array_val = array; psc->n_values++; } static void internal_FontDirectory (Gt1PSContext *psc) { ensure_stack (psc, 1); psc->value_stack[psc->n_values].type = GT1_VAL_DICT; psc->value_stack[psc->n_values].val.dict_val = psc->fonts; psc->n_values++; } /* the table of internal procedures */ typedef struct _InternalGt1ProcListing { char *name; void (*function) (Gt1PSContext *psc); } InternalGt1ProcListing; InternalGt1ProcListing internal_procs[] = { { "dict", internal_dict }, { "begin", internal_begin }, { "end", internal_end }, { "dup", internal_dup }, { "pop", internal_pop }, { "exch", internal_exch }, { "readonly", internal_readonly }, { "executeonly", internal_executeonly }, { "noaccess", internal_noaccess }, { "def", internal_def }, { "false", internal_false }, { "true", internal_true }, { "StandardEncoding", internal_StandardEncoding }, { "[", internalop_openbracket }, { "]", internalop_closebracket }, { "currentdict", internal_currentdict }, { "currentfile", internal_currentfile }, { "eexec", internal_eexec }, { "array", internal_array }, { "string", internal_string }, { "readstring", internal_readstring }, { "put", internal_put }, { "get", internal_get }, { "index", internal_index }, { "definefont", internal_definefont }, { "mark", internal_mark }, { "closefile", internal_closefile }, { "cleartomark", internal_cleartomark }, { "systemdict", internal_systemdict }, { "userdict", internal_userdict }, { "known", internal_known }, { "ifelse", internal_ifelse }, { "if", internal_if }, { "for", internal_for }, { "not", internal_not }, { "bind", internal_bind }, { "exec", internal_exec }, { "count", internal_count }, { "eq", internal_eq }, { "ne", internal_ne }, { "type", internal_type }, { "cvx", internal_cvx }, { "matrix", internal_matrix }, { "FontDirectory", internal_FontDirectory } }; /* here end the internal procedures */ static Gt1PSContext * pscontext_new (Gt1TokenContext *tc) { Gt1PSContext *psc; Gt1Dict *systemdict; Gt1Dict *globaldict; Gt1Dict *userdict; int i; Gt1Value val; psc = gt1_new (Gt1PSContext, 1); psc->r = gt1_region_new (); psc->tc = tc; psc->nc = gt1_name_context_new (); psc->n_values = 0; psc->n_values_max = 16; psc->value_stack = gt1_new (Gt1Value, psc->n_values_max); psc->n_dicts_max = 16; psc->gt1_dict_stack = gt1_new (Gt1Dict *, psc->n_dicts_max); systemdict = gt1_dict_new (psc->r, sizeof(internal_procs) / sizeof(InternalGt1ProcListing)); for (i = 0; i < sizeof(internal_procs) / sizeof(InternalGt1ProcListing); i++) { val.type = GT1_VAL_INTERNAL; val.val.internal_val = internal_procs[i].function; gt1_dict_def (psc->r, systemdict, gt1_name_context_intern (psc->nc, internal_procs[i].name), &val); } psc->gt1_dict_stack[0] = systemdict; globaldict = gt1_dict_new (psc->r, 16); psc->gt1_dict_stack[1] = globaldict; userdict = gt1_dict_new (psc->r, 16); psc->gt1_dict_stack[2] = userdict; psc->n_dicts = 3; psc->fonts = gt1_dict_new (psc->r, 1); psc->n_files_max = 16; psc->file_stack = gt1_new (Gt1TokenContext *, psc->n_files_max); psc->file_stack[0] = tc; psc->n_files = 1; psc->quit = 0; return psc; } static void pscontext_free (Gt1PSContext *psc) { /* Empty the value stack. */ while (psc->n_values > 0) internal_pop (psc); gt1_free (psc->value_stack); gt1_free (psc->file_stack); gt1_free (psc->gt1_dict_stack); gt1_name_context_free (psc->nc); gt1_region_free (psc->r); gt1_free (psc); } static void eval_executable (Gt1PSContext *psc, Gt1Value *val) { switch (val->type) { case GT1_VAL_INTERNAL: val->val.internal_val (psc); break; case GT1_VAL_PROC: eval_proc (psc, val->val.proc_val); break; default: ensure_stack (psc, 1); psc->value_stack[psc->n_values] = *val; psc->n_values++; break; } } /* Scan a value from the psc's token context - usually one token, or a proc. Return the token type. */ static TokenType parse_ps_token (Gt1PSContext *psc, Gt1Value *val) { MyGt1String lexeme; TokenType type; Gt1Proc *proc; int n_proc, n_proc_max; type = tokenize_get (psc->tc, &lexeme); switch (type) { case TOK_NUM: val->type = GT1_VAL_NUM; val->val.num_val = parse_num (&lexeme); break; case TOK_NAME: val->type = GT1_VAL_NAME; val->val.name_val = gt1_name_context_intern_size (psc->nc, lexeme.start, lexeme.fin - lexeme.start); break; case TOK_STR: val->type = GT1_VAL_STR; /* todo: processing of escape characters */ val->val.str_val.start = lexeme.start; val->val.str_val.size = lexeme.fin - lexeme.start; break; case TOK_IDENT: val->type = GT1_VAL_UNQ_NAME; val->val.name_val = gt1_name_context_intern_size (psc->nc, lexeme.start, lexeme.fin - lexeme.start); break; case TOK_OPENBRACE: n_proc = 0; n_proc_max = 16; proc = (Gt1Proc *)gt1_region_alloc (psc->r, sizeof(Gt1Proc) + (n_proc_max - 1) * sizeof(Gt1Value)); while (1) { if (n_proc == n_proc_max) { int old_size; old_size = sizeof(Gt1Proc) + (n_proc_max - 1) * sizeof(Gt1Value); n_proc_max <<= 1; proc = (Gt1Proc *)gt1_region_realloc (psc->r, proc, old_size, sizeof(Gt1Proc) + (n_proc_max - 1) * sizeof(Gt1Value)); } if (parse_ps_token (psc, &proc->vals[n_proc]) == TOK_CLOSEBRACE || psc->quit) break; n_proc++; } proc->n_values = n_proc; val->type = GT1_VAL_PROC; val->val.proc_val = proc; break; case TOK_CLOSEBRACE: case TOK_END: break; default: printf ("unimplemented token type\n"); psc->quit = 1; break; } return type; } static void eval_ps_val (Gt1PSContext *psc, Gt1Value *val) { Gt1Value *new_val; #ifdef VERBOSE print_value (psc, val); printf ("\n"); #endif switch (val->type) { case GT1_VAL_NUM: case GT1_VAL_BOOL: case GT1_VAL_STR: case GT1_VAL_NAME: case GT1_VAL_ARRAY: case GT1_VAL_PROC: case GT1_VAL_DICT: ensure_stack (psc, 1); psc->value_stack[psc->n_values] = *val; psc->n_values++; break; case GT1_VAL_UNQ_NAME: new_val = gt1_dict_stack_lookup (psc, val->val.name_val); if (new_val != NULL) eval_executable (psc, new_val); else { printf ("undefined identifier "); print_value (psc, val); putchar ('\n'); psc->quit = 1; } break; case GT1_VAL_INTERNAL: val->val.internal_val (psc); break; default: printf ("value not handled\n"); psc->quit = 1; break; } #ifdef VERBOSE if (!psc->quit) { printf (" "); print_stack (psc); } #endif } /* maybe we should return the dict; easier to handle */ static Gt1PSContext * eval_ps (Gt1TokenContext *tc) { TokenType type; Gt1PSContext *psc; Gt1Value val; psc = pscontext_new (tc); do { type = parse_ps_token (psc, &val); if (type == TOK_END) break; if (type == TOK_CLOSEBRACE) { printf ("unexpected close brace\n"); break; } eval_ps_val (psc, &val); } while (!psc->quit); return psc; } /* This routine _assumes_ that plaintext is passed in with enough space to hold the decrypted text */ static void charstring_decrypt (Gt1String *plaintext, Gt1String *ciphertext) { int ciphertext_size; int i; unsigned short r; unsigned char cipher; unsigned char plain; ciphertext_size = ciphertext->size; if (plaintext->size < ciphertext_size - 4) { printf ("not enough space allocated for charstring decryption\n"); return; } r = 4330; /* initial key */ for (i = 0; i < ciphertext_size; i++) { cipher = ciphertext->start[i]; plain = (cipher ^ (r>>8)); r = (cipher + r) * EEXEC_C1 + EEXEC_C2; if (i >= 4) plaintext->start[i - 4] = plain; } plaintext->size = ciphertext->size - 4; } #ifdef VERBOSE static void print_glyph_code (Gt1String *glyph_code) { Gt1String plaintext; int i; int byte, byte1, byte2, byte3, byte4; plaintext.start = gt1_alloc (glyph_code->size); plaintext.size = glyph_code->size; charstring_decrypt (&plaintext, glyph_code); for (i = 0; i < plaintext.size; i++) { byte = ((unsigned char *)plaintext.start)[i]; if (byte >= 32 && byte <= 246) printf (" %d", byte - 139); else if (byte >= 247 && byte <= 250) { byte1 = ((unsigned char *)plaintext.start)[++i]; printf (" %d", ((byte - 247) << 8) + byte1 + 108); } else if (byte >= 251 && byte <= 254) { byte1 = ((unsigned char *)plaintext.start)[++i]; printf (" %d", -((byte - 251) << 8) - byte1 - 108); } else if (byte == 255) { byte1 = ((unsigned char *)plaintext.start)[++i]; byte2 = ((unsigned char *)plaintext.start)[++i]; byte3 = ((unsigned char *)plaintext.start)[++i]; byte4 = ((unsigned char *)plaintext.start)[++i]; /* warning: this _must_ be a 32 bit int - alpha portability issue! */ printf (" %d", (byte1 << 24) + (byte2 << 16) + (byte3 << 8) + byte4); } else if (byte == 12) { byte1 = ((unsigned char *)plaintext.start)[++i]; if (byte1 == 6) printf (" seac"); else if (byte1 == 7) printf (" sbw"); else if (byte1 == 0) printf (" dotsection"); else if (byte1 == 2) printf (" hstem3"); else if (byte1 == 1) printf (" vstem3"); else if (byte1 == 12) printf (" div"); else if (byte1 == 16) printf (" callothersubr"); else if (byte1 == 17) printf (" pop"); else if (byte1 == 33) printf (" setcurrentpoint"); else printf (" esc%d", byte1); } else if (byte == 14) printf (" endchar"); else if (byte == 13) printf (" hsbw"); else if (byte == 9) printf (" closepath"); else if (byte == 6) printf (" hlineto"); else if (byte == 22) printf (" hmoveto"); else if (byte == 31) printf (" hvcurveto"); else if (byte == 5) printf (" rlineto"); else if (byte == 21) printf (" rmoveto"); else if (byte == 8) printf (" rrcurveto"); else if (byte == 30) printf (" vhcurveto"); else if (byte == 7) printf (" vlineto"); else if (byte == 4) printf (" vmoveto"); else if (byte == 1) printf (" hstem"); else if (byte == 3) printf (" vstem"); else if (byte == 10) printf (" callsubr"); else if (byte == 11) printf (" return"); else printf (" com%d", byte); } printf ("\n"); gt1_free (plaintext.start); } #endif /* Gt1Dict is the toplevel font dict. This allocates a new string for the plaintext and stores it in body. */ static void get_subr_body (Gt1PSContext *psc, Gt1String *body, Gt1Dict *fontdict, int subr) { Gt1Value *private_val; Gt1Value *subrs_val; Gt1Array *subrs_array; Gt1String *ciphertext; private_val = gt1_dict_lookup (fontdict, gt1_name_context_intern (psc->nc, "Private")); if (private_val == NULL) { printf ("No Private array\n"); return; } subrs_val = gt1_dict_lookup (private_val->val.dict_val, gt1_name_context_intern (psc->nc, "Subrs")); if (subrs_val == NULL) { printf ("No Subrs array\n"); return; } subrs_array = subrs_val->val.array_val; /* more type & range checking */ ciphertext = &subrs_array->vals[subr].val.str_val; body->start = gt1_alloc (ciphertext->size); body->size = ciphertext->size; charstring_decrypt (body, ciphertext); } typedef struct _BezState { ArtBpath *bezpath; int size_bezpath, size_bezpath_max; /* this is designed to do compression of a string of moveto's in a row. */ int need_moveto; double x, y; /* current point */ double x0, y0; /* beginning of subpath */ } BezState; static BezState * bs_new (void) { BezState *bs; bs = gt1_new (BezState, 1); bs->size_bezpath = 0; bs->size_bezpath_max = 16; bs->bezpath = gt1_new (ArtBpath, bs->size_bezpath_max); bs->x = 0; bs->y = 0; bs->x0 = 0; bs->y0 = 0; bs->need_moveto = 1; return bs; } static void bs_moveto (BezState *bs, double x, double y) { bs->x = x; bs->y = y; bs->need_moveto = 1; } static void bs_rmoveto (BezState *bs, double dx, double dy) { bs->x += dx; bs->y += dy; bs->need_moveto = 1; } static void bs_do_moveto (BezState *bs) { ArtBpath *bezpath; int size_bezpath; if (bs->need_moveto) { bezpath = bs->bezpath; size_bezpath = bs->size_bezpath; if (size_bezpath == bs->size_bezpath_max) { gt1_double (bezpath, ArtBpath, bs->size_bezpath_max); bs->bezpath = bezpath; } bezpath[size_bezpath].code = ART_MOVETO; bezpath[size_bezpath].x1 = 0; bezpath[size_bezpath].y1 = 0; bezpath[size_bezpath].x2 = 0; bezpath[size_bezpath].y2 = 0; bezpath[size_bezpath].x3 = bs->x; bezpath[size_bezpath].y3 = bs->y; bs->size_bezpath++; bs->x0 = bs->x; bs->y0 = bs->y; bs->need_moveto = 0; } } static void bs_rlineto (BezState *bs, double dx, double dy) { ArtBpath *bezpath; int size_bezpath; bs_do_moveto (bs); bezpath = bs->bezpath; size_bezpath = bs->size_bezpath; if (size_bezpath == bs->size_bezpath_max) { gt1_double (bezpath, ArtBpath, bs->size_bezpath_max); bs->bezpath = bezpath; } bezpath[size_bezpath].code = ART_LINETO; bezpath[size_bezpath].x1 = 0; bezpath[size_bezpath].y1 = 0; bezpath[size_bezpath].x2 = 0; bezpath[size_bezpath].y2 = 0; bs->x += dx; bs->y += dy; bezpath[size_bezpath].x3 = bs->x; bezpath[size_bezpath].y3 = bs->y; bs->size_bezpath++; } static void bs_rcurveto (BezState *bs, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3) { ArtBpath *bezpath; int size_bezpath; double x, y; bs_do_moveto (bs); bezpath = bs->bezpath; size_bezpath = bs->size_bezpath; if (size_bezpath == bs->size_bezpath_max) { gt1_double (bezpath, ArtBpath, bs->size_bezpath_max); bs->bezpath = bezpath; } bezpath[size_bezpath].code = ART_CURVETO; x = bs->x + dx1; y = bs->y + dy1; bezpath[size_bezpath].x1 = x; bezpath[size_bezpath].y1 = y; x += dx2; y += dy2; bezpath[size_bezpath].x2 = x; bezpath[size_bezpath].y2 = y; x += dx3; y += dy3; bezpath[size_bezpath].x3 = x; bezpath[size_bezpath].y3 = y; bs->x = x; bs->y = y; bs->size_bezpath++; } /* this is for callothersubr (2) */ static void bs_curveto (BezState *bs, double flexbuf[6]) { ArtBpath *bezpath; int size_bezpath; bs->need_moveto = 0; bezpath = bs->bezpath; size_bezpath = bs->size_bezpath; if (size_bezpath == bs->size_bezpath_max) { gt1_double (bezpath, ArtBpath, bs->size_bezpath_max); bs->bezpath = bezpath; } bezpath[size_bezpath].code = ART_CURVETO; bezpath[size_bezpath].x1 = flexbuf[0]; bezpath[size_bezpath].y1 = flexbuf[1]; bezpath[size_bezpath].x2 = flexbuf[2]; bezpath[size_bezpath].y2 = flexbuf[3]; bezpath[size_bezpath].x3 = flexbuf[4]; bezpath[size_bezpath].y3 = flexbuf[5]; bs->size_bezpath++; } static void bs_closepath (BezState *bs) { ArtBpath *bezpath; int size_bezpath; if (bs->x0 == bs->x && bs->y0 == bs->y) return; bezpath = bs->bezpath; size_bezpath = bs->size_bezpath; if (size_bezpath == bs->size_bezpath_max) { gt1_double (bezpath, ArtBpath, bs->size_bezpath_max); bs->bezpath = bezpath; } bezpath[size_bezpath].code = ART_LINETO; bezpath[size_bezpath].x1 = 0; bezpath[size_bezpath].y1 = 0; bezpath[size_bezpath].x2 = 0; bezpath[size_bezpath].y2 = 0; bezpath[size_bezpath].x3 = bs->x0; bezpath[size_bezpath].y3 = bs->y0; bs->size_bezpath++; } static ArtBpath * bs_end (BezState *bs) { ArtBpath *bezpath; int size_bezpath; bezpath = bs->bezpath; size_bezpath = bs->size_bezpath; if (size_bezpath == bs->size_bezpath_max) gt1_double (bezpath, ArtBpath, bs->size_bezpath_max); bezpath[size_bezpath].code = ART_END; bezpath[size_bezpath].x1 = 0; bezpath[size_bezpath].y1 = 0; bezpath[size_bezpath].x2 = 0; bezpath[size_bezpath].y2 = 0; bezpath[size_bezpath].x3 = 0; bezpath[size_bezpath].y3 = 0; gt1_free (bs); return bezpath; } static ArtBpath * convert_glyph_code_to_begt1_path (Gt1PSContext *psc, Gt1String *glyph_code, Gt1Dict *fontdict, double *p_wx) { BezState *bs; int i; int byte, byte1, byte2, byte3, byte4; double stack[256]; int stack_ptr; double ps_stack[16]; int ps_stack_ptr; Gt1String exe_stack[10]; int ret_stack[10]; int exe_stack_ptr; int subr; int val; /* needs to be int32! */ /* stuff for flex */ double flexbuf[6]; int flexptr = -1; exe_stack_ptr = 0; exe_stack[0].start = gt1_alloc (glyph_code->size); exe_stack[0].size = glyph_code->size; charstring_decrypt (&exe_stack[0], glyph_code); bs = bs_new (); ps_stack_ptr = 0; stack_ptr = 0; for (i = 0; exe_stack_ptr || i < exe_stack[exe_stack_ptr].size; i++) { if (stack_ptr >= 240) goto error; byte = ((unsigned char *)exe_stack[exe_stack_ptr].start)[i]; if (byte >= 32 && byte <= 246) stack[stack_ptr++] = byte - 139; else if (byte >= 247 && byte <= 250) { byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i]; stack[stack_ptr++] = ((byte - 247) << 8) + byte1 + 108; } else if (byte >= 251 && byte <= 254) { byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i]; stack[stack_ptr++] = -((byte - 251) << 8) - byte1 - 108; } else if (byte == 255) { byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i]; byte2 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i]; byte3 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i]; byte4 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i]; val = (byte1 << 24) + (byte2 << 16) + (byte3 << 8) + byte4; stack[stack_ptr++] = val; } else if (byte == 12) { byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i]; if (byte1 == 6) printf (" seac"); else if (byte1 == 7) printf (" sbw"); else if (byte1 == 0) { /* dotsection */ } else if (byte1 == 2) { /* hstem3 */ stack_ptr -= 6; } else if (byte1 == 1) { /* vstem3 */ stack_ptr -= 6; } else if (byte1 == 12) { /* div */ if (stack_ptr < 2) goto error; if (stack[stack_ptr - 1] == 0) goto error; stack[stack_ptr - 2] = stack[stack_ptr - 2] / stack[stack_ptr - 1]; stack_ptr--; } else if (byte1 == 16) { /* callothersubr */ int j; int subr; int n_args; if (stack_ptr < 2) goto error; subr = (int)stack[--stack_ptr]; n_args = (int)stack[--stack_ptr]; if (stack_ptr < n_args) goto error; if (ps_stack_ptr + n_args > 16) goto error; for (j = 0; j < n_args; j++) ps_stack[ps_stack_ptr++] = stack[--stack_ptr]; #ifdef VERBOSE printf ("%d %d callothersubr\nPS:", n_args, subr); for (j = 0; j < ps_stack_ptr; j++) printf (" %g", ps_stack[j]); printf ("\nstack:"); for (j = 0; j < stack_ptr; j++) printf (" %g", stack[j]); printf ("\n"); #endif #undef VERBOSE if (subr == 3) { if (ps_stack_ptr < 1) goto error; ps_stack[ps_stack_ptr - 1] = 3; } else if (subr == 0) { if (ps_stack_ptr < 3) goto error; ps_stack_ptr--; } else if (subr == 1) { bs_do_moveto (bs); flexptr = -2; } else if (subr == 2) { if (flexptr >= 0) { flexbuf[flexptr] = bs->x; flexbuf[flexptr + 1] = bs->y; } flexptr += 2; if (flexptr == 6) { bs_curveto (bs, flexbuf); flexptr = 0; } } } else if (byte1 == 17) { /* pop */ if (ps_stack_ptr == 0) goto error; stack[stack_ptr++] = ps_stack[--ps_stack_ptr]; } else if (byte1 == 33) { /* setcurrentpoint */ #ifdef VERBOSE printf ("%g %g setcurrentpoint, bs is %g, %g\n", stack[stack_ptr - 2], stack[stack_ptr - 1], bs->x, bs->y); #endif bs->x = stack[stack_ptr - 2]; bs->y = stack[stack_ptr - 1]; stack_ptr -= 2; } else printf (" esc%d", byte1); } else if (byte == 14) { /* endchar */ /* nothing really to do here, except maybe some sanitychecking */ } else if (byte == 13) { /* hsbw */ bs_moveto (bs, stack[stack_ptr - 2], 0); if (p_wx) *p_wx = stack[stack_ptr - 1]; stack_ptr -= 2; } else if (byte == 9) { /* closepath */ bs_closepath (bs); } else if (byte == 6) { /* hlineto */ bs_rlineto (bs, stack[--stack_ptr], 0); } else if (byte == 22) { /* hmoveto */ bs_rmoveto (bs, stack[--stack_ptr], 0); } else if (byte == 31) { /* hvcurveto */ bs_rcurveto (bs, stack[stack_ptr - 4], 0, stack[stack_ptr - 3], stack[stack_ptr - 2], 0, stack[stack_ptr - 1]); stack_ptr -= 4; } else if (byte == 5) { /* rlineto */ bs_rlineto (bs, stack[stack_ptr - 2], stack[stack_ptr - 1]); stack_ptr -= 2; } else if (byte == 21) { /* rmoveto */ bs_rmoveto (bs, stack[stack_ptr - 2], stack[stack_ptr - 1]); stack_ptr -= 2; } else if (byte == 8) { /* rrcurveto */ bs_rcurveto (bs, stack[stack_ptr - 6], stack[stack_ptr - 5], stack[stack_ptr - 4], stack[stack_ptr - 3], stack[stack_ptr - 2], stack[stack_ptr - 1]); stack_ptr -= 6; } else if (byte == 30) { /* vhcurveto */ bs_rcurveto (bs, 0, stack[stack_ptr - 4], stack[stack_ptr - 3], stack[stack_ptr - 2], stack[stack_ptr - 1], 0); stack_ptr -= 4; } else if (byte == 7) { /* vlineto */ bs_rlineto (bs, 0, stack[--stack_ptr]); } else if (byte == 4) { /* vmoveto */ bs_rmoveto (bs, 0, stack[--stack_ptr]); } else if (byte == 1) { /* hstem */ stack_ptr -= 2; } else if (byte == 3) { /* vstem */ stack_ptr -= 2; } else if (byte == 10) { /* callsubr */ subr = (int)stack[--stack_ptr]; #ifdef VERBOSE printf (" /* call subr %d */\n", subr); #endif ret_stack[exe_stack_ptr] = i; exe_stack_ptr++; if (exe_stack_ptr == 10) goto error; get_subr_body (psc, &exe_stack[exe_stack_ptr], fontdict, subr); i = -1; } else if (byte == 11) { /* return */ #ifdef VERBOSE printf (" /* return */\n"); #endif /* check exe_stack_ptr */ gt1_free (exe_stack[exe_stack_ptr].start); exe_stack_ptr--; i = ret_stack[exe_stack_ptr]; } else printf (" com%d", byte); } #ifdef VERBOSE printf ("\n"); #endif gt1_free (exe_stack[0].start); if (stack_ptr != 0) printf ("warning: stack_ptr = %d\n", stack_ptr); if (ps_stack_ptr != 0) printf ("warning: ps_stack_ptr = %d\n", ps_stack_ptr); return bs_end (bs); error: gt1_free (bs_end (bs)); return NULL; } struct _Gt1LoadedFont { char* filename; Gt1PSContext *psc; #ifdef AFM Font_Info *fi; MunchedFontInfo *mfi; #endif Gt1Dict *fontdict; Gt1NameId id_charstrings; Gt1LoadedFont *next; /*chain of known fonts*/ }; static Gt1LoadedFont *_loadedFonts=NULL; struct _Gt1EncodedFont { Gt1LoadedFont *font; Gt1NameId *encoding; size_t n; /*length of the encoding*/ char *name; /*external font name*/ Gt1EncodedFont *next; /*chain of known fonts*/ }; static Gt1EncodedFont *_encodedFonts=NULL; #ifdef AFM /* allocate a new filename, same as the old one, but with the extension */ static char * replace_extension (const char *filename, const char *ext) { int i; int size_fn, size_ext; char *new_fn; size_fn = strlen (filename); size_ext = strlen (ext); for (i = size_fn - 1; i >= 0; i--) if (filename[i] == '.' || filename[i] == '/') break; if (filename[i] != '.') i = size_fn; new_fn = gt1_new (char, i + size_ext + 1); memcpy (new_fn, filename, i); memcpy (new_fn + i, ext, size_ext); new_fn[i + size_ext] = '\0'; return new_fn; } /* some functions for dealing with defined fonts after evaluation of the font program is complete. */ typedef struct _MunchedFontInfo MunchedFontInfo; typedef struct _KernPair KernPair; /* The kern pair table is actually a hash table */ struct _MunchedFontInfo { int kern_pair_table_size; KernPair *kern_pair_table; }; struct _KernPair { Gt1NameId name1; /* or -1 if empty */ Gt1NameId name2; int xamt, yamt; }; #define kern_pair_hash(n1,n2) ((n1) * 367 + (n2)) static MunchedFontInfo * munch_font_info (Gt1PSContext *psc, Font_Info *fi) { MunchedFontInfo *mfi; KernPair *table; int table_size; int i, j; Gt1NameId name1, name2; mfi = gt1_new (MunchedFontInfo, 1); table_size = fi->numOfPairs << 1; mfi->kern_pair_table_size = table_size; table = gt1_new (KernPair, table_size); mfi->kern_pair_table = table; for (i = 0; i < mfi->kern_pair_table_size; i++) table[i].name1 = -1; /* Transfer afm kern pair information into the hash table, taking care to intern the names as we go. */ for (i = 0; i < fi->numOfPairs; i++) { name1 = gt1_name_context_intern (psc->nc, fi->pkd[i].name1); name2 = gt1_name_context_intern (psc->nc, fi->pkd[i].name2); for (j = kern_pair_hash (name1, name2); table[j % table_size].name1 != -1; j++); j = j % table_size; table[j].name1 = name1; table[j].name2 = name2; table[j].xamt = fi->pkd[i].xamt; table[j].yamt = fi->pkd[i].yamt; } return mfi; } static void free_munched_font_info(MunchedFontInfo *mfi) { gt1_free (mfi->kern_pair_table); gt1_free (mfi); } static int try_read_afm(Gt1LoadedFont *font) { char *afm_filename; FILE *afm_f; int r, status; afm_filename = replace_extension(font->filename, ".afm"); afm_f = fopen(afm_filename, "rb"); if((r=(afm_f!= NULL))){ status = parseFile(afm_f, &font->fi, P_GALL); fclose (afm_f); font->mfi = munch_font_info(psc, font->fi); } gt1_free (afm_filename); } /* get xamt of kern pair */ double gt1_get_kern_pair(Gt1LoadedFont *font, int glyph1, int glyph2) { Gt1NameId name1, name2; int i, idx; KernPair *table; int table_size; if (font == NULL) return 0; name1 = font->encoding[glyph1 & 0xff]; name2 = font->encoding[glyph2 & 0xff]; table_size = font->mfi->kern_pair_table_size; table = font->mfi->kern_pair_table; for (i = kern_pair_hash (name1, name2); idx = i % table_size, table[idx].name1 != -1; i++) if (table[idx].name1 == name1 && table[idx].name2 == name2) return table[idx].xamt; return 0; } int gt1_is_symbol_font(Gt1LoadedFont *font) { char* name = gt1_get_font_name(font); return stricmp(name,"symbol")==0 || stricmp(name,"zapfdingbats")==0; } char * gt1_get_font_name (Gt1LoadedFont *font) { return font->fi->gfi->fontName; } #endif #if defined(_WIN32) # define STRNICMP(a,b,n) strnicmp(a,b,n) #endif Gt1EncodedFont* gt1_get_encoded_font(char* name) { Gt1EncodedFont *e = _encodedFonts; while(e && strcmp(name,e->name)) e = e->next; return e; } char* gt1_encoded_font_name(Gt1EncodedFont* e) { return e->name; } static void _gt1_del_encodedFont (Gt1EncodedFont *font) { gt1_free(font->encoding); gt1_free(font->name); } Gt1EncodedFont* gt1_create_encoded_font(char* name, char *pfbPath, char **names, int n, gt1_encapsulated_read_func_t *reader) { int i=0; Gt1EncodedFont *e; Gt1LoadedFont *f; Gt1NameId* enc; Gt1NameId _notdef; /*find the pfb info (possibly cached)*/ f = gt1_load_font(pfbPath,reader); if(!f) return NULL; if((e = gt1_get_encoded_font(name))){ _gt1_del_encodedFont(e); } else { e = gt1_new(Gt1EncodedFont, 1); } enc = gt1_new(Gt1NameId, n); e->encoding = enc; e->n = n; e->font = f; e->name = strdup(name); _notdef = gt1_name_context_interned(f->psc->nc, ".notdef"); for(i=0;ipsc->nc, names[i]) : _notdef; enc[i] = v!=GT1_UNKNOWN? v : _notdef; } e->next = _encodedFonts; _encodedFonts = e; return e; } Gt1LoadedFont *gt1_load_font(const char *filename, gt1_encapsulated_read_func_t *reader) { Gt1LoadedFont *font; Gt1Dict *fontdict; char *pfb; int pfb_size; Gt1TokenContext *tc; Gt1PSContext *psc; char *flat; font = _loadedFonts; while(font){ if(!strcmp(filename,font->filename)){ return font; } font = font->next; } pfb = reader ? reader->reader(reader->data,filename,&pfb_size) : NULL; if(!pfb){ int pfb_size_max, bytes_read; FILE *f; f = fopen(filename, "rb"); if(f==NULL) return NULL; pfb_size = 0; pfb_size_max = 32768; pfb = gt1_new(char, pfb_size_max); while(1){ bytes_read = fread(pfb + pfb_size, 1, pfb_size_max - pfb_size, f); if(bytes_read == 0) break; pfb_size += bytes_read; gt1_double(pfb, char, pfb_size_max); } fclose(f); } /* fwrite (pfb, 1, pfb_size, stdout); */ /* this is a good place to do a "magic" computation on the input file. */ if(pfb_size) { if (((unsigned char *)pfb)[0] == 128) flat = pfb_to_flat (pfb, pfb_size); else { flat = gt1_new (char, pfb_size + 1); memcpy (flat, pfb, pfb_size); flat[pfb_size] = 0; } } else { flat = gt1_new (char, 1); *flat = 0; } gt1_free (pfb); /* printf ("%s", flat); */ /* test_token (flat); */ tc = tokenize_new (flat); gt1_free (flat); psc = eval_ps(tc); tokenize_free (tc); if (psc->fonts->n_entries != 1) { pscontext_free (psc); return NULL; } font = gt1_new (Gt1LoadedFont, 1); font->filename = strdup(filename); font->psc = psc; fontdict = psc->fonts->entries[0].val.val.dict_val; font->fontdict = fontdict; #ifdef AFM font->fi = NULL; font->mfi = NULL; #endif font->id_charstrings = gt1_name_context_intern (psc->nc, "CharStrings"); font->next = _loadedFonts; _loadedFonts = font; return font; } void gt1_unload_font (Gt1LoadedFont *font) { pscontext_free (font->psc); gt1_free(font->filename); #ifdef AFM if (font->mfi) free_munched_font_info(font->mfi); if (font->fi) parseFileFree(font->fi); #endif gt1_free (font); } void gt1_del_encodedFont (Gt1EncodedFont *font) { _gt1_del_encodedFont(font); gt1_free(font); } void gt1_del_cache(void) { while(_encodedFonts){ Gt1EncodedFont *e = _encodedFonts; _encodedFonts = _encodedFonts->next; gt1_del_encodedFont(e); } while(_loadedFonts){ Gt1LoadedFont *e = _loadedFonts; _loadedFonts = _loadedFonts->next; gt1_unload_font(e); } } ArtBpath *_get_glyph_outline(Gt1LoadedFont *font, Gt1NameId glyphname, double *p_wx) { Gt1Value *charstringsval, *glyphcodeval; Gt1Dict *charstrings; Gt1String *glyphcode; /* charstringsval is a structure w/a union */ charstringsval = gt1_dict_lookup (font->fontdict, font->id_charstrings); charstrings = charstringsval->val.dict_val; glyphcodeval = gt1_dict_lookup (charstrings, glyphname); if (glyphcodeval) { glyphcode = &glyphcodeval->val.str_val; #ifdef VERBOSE /* note: there is an #undef VERBOSE above, so this is probably going to be ineffective. */ print_glyph_code (glyphcode); #endif return convert_glyph_code_to_begt1_path(font->psc, glyphcode, font->fontdict, p_wx); } else { /* fprintf(stderr, "didn't get glyphcodeval, returning NULL\n"); */ return NULL; } } ArtBpath *gt1_get_glyph_outline(Gt1EncodedFont *font, int glyphnum, double *p_wx) { return (glyphnum<0 || glyphnum>(int)font->n) ? NULL : _get_glyph_outline(font->font,font->encoding[glyphnum],p_wx); } #ifdef MAIN int main (int argc, char **argv) { #if 1 int i; for(i=1;i /*for memcpy etc*/ #define REGION_BLOCK_SIZE 4096 typedef struct _Gt1RegionBlock Gt1RegionBlock; struct _Gt1RegionBlock { Gt1RegionBlock *next; void *dummy; /* this helps alignment */ /* actual memory here */ }; struct _Gt1Region { Gt1RegionBlock *first; Gt1RegionBlock *last; char *alloc_ptr; int space_left; /* number of bytes left in last block */ }; Gt1Region * gt1_region_new (void) { Gt1Region *r; Gt1RegionBlock *rb; r = gt1_new (Gt1Region, 1); rb = (Gt1RegionBlock *)gt1_alloc (sizeof(Gt1RegionBlock) + REGION_BLOCK_SIZE); rb->next = NULL; r->first = rb; r->last = rb; r->alloc_ptr = ((char *)rb) + sizeof(Gt1RegionBlock); r->space_left = REGION_BLOCK_SIZE; return r; } void * gt1_region_alloc (Gt1Region *r, int size) { Gt1RegionBlock *rb; int pad_size; pad_size = (size + 7) & ~7; if (pad_size >= REGION_BLOCK_SIZE) { /* concatentate to front of list and leave last block alone */ rb = (Gt1RegionBlock *)gt1_alloc (sizeof(Gt1RegionBlock) + size); rb->next = r->first; r->first = rb; return (void*)(((char *)rb) + sizeof(Gt1RegionBlock)); } else if (pad_size > r->space_left) { /* allocate a new block */ rb = (Gt1RegionBlock *)gt1_alloc (sizeof(Gt1RegionBlock) + REGION_BLOCK_SIZE); rb->next = NULL; r->last->next = rb; r->last = rb; r->alloc_ptr = ((char *)rb) + sizeof(Gt1RegionBlock) + pad_size; r->space_left = REGION_BLOCK_SIZE - pad_size; return (void*)(((char *)rb) + sizeof(Gt1RegionBlock)); } else { /* allocate in last block */ char *p; p = r->alloc_ptr; r->alloc_ptr += pad_size; r->space_left -= pad_size; return (void *)p; } } void * gt1_region_realloc (Gt1Region *r, void *p, int old_size, int size) { char *new; if (old_size >= size) return p; new = gt1_region_alloc (r, size); memcpy (new, p, old_size); return new; } void gt1_region_free (Gt1Region *r) { Gt1RegionBlock *rb, *next; for (rb = r->first; rb != NULL; rb = next) { next = rb->next; gt1_free (rb); } gt1_free (r); } rl-renderpm-4.0.3/src/gt1/gt1-region.h000066400000000000000000000004171453236046100173620ustar00rootroot00000000000000/* A simple region-based allocator */ typedef struct _Gt1Region Gt1Region; Gt1Region *gt1_region_new (void); void *gt1_region_alloc (Gt1Region *r, int size); void *gt1_region_realloc (Gt1Region *r, void *p, int old_size, int size); void gt1_region_free (Gt1Region *r); rl-renderpm-4.0.3/src/gt1/gt1-value.h000066400000000000000000000022411453236046100172100ustar00rootroot00000000000000/* Basic value datatypes for implementing the PostScript language */ /* Note: this pulls in a lot of datatypes from the interpreter. That's not really clean. Oh well. */ typedef struct _Gt1Value Gt1Value; typedef struct _Gt1Dict Gt1Dict; typedef struct _Gt1Array Gt1Array; typedef struct _Gt1Array Gt1Proc; typedef struct _Gt1String Gt1String; typedef struct _Gt1PSContext Gt1PSContext; typedef struct _Gt1TokenContext Gt1TokenContext; typedef enum { GT1_VAL_NUM, GT1_VAL_BOOL, GT1_VAL_STR, GT1_VAL_NAME, GT1_VAL_UNQ_NAME, GT1_VAL_DICT, GT1_VAL_INTERNAL, GT1_VAL_ARRAY, GT1_VAL_PROC, GT1_VAL_FILE, GT1_VAL_MARK } Gt1ValueType; struct _Gt1String { char *start; int size; }; struct _Gt1Value { Gt1ValueType type; union { double num_val; int bool_val; Gt1String str_val; Gt1NameId name_val; Gt1NameId unq_name_val; /* unquoted name - I don't really understand this */ void (*internal_val)(Gt1PSContext *psc); Gt1Dict *dict_val; Gt1Array *array_val; Gt1Proc *proc_val; Gt1TokenContext *file_val; /* to be replaced with a tag, hopefully */ } val; }; struct _Gt1Array { int n_values; Gt1Value vals[1]; }; rl-renderpm-4.0.3/src/gt1/parseAFM.c000066400000000000000000001307761453236046100170530ustar00rootroot00000000000000/* Our mods: 1. FontInfo has become Font_Info to avoid namespace collisions. 2. Version is a linetoken because Bitstream Charter has a space in the version. 3. Added some necessary #include headers. 4. Added AFM_ prefixes to error codes. 5. Made it recognize both '\n' and '\r' as line terminators. Sheesh! 6. Stopped buffer overflows in token and linetoken. Raph Levien writing on 4 Oct 1998, updating 21 Oct 1998 1. parseFileFree function. 2. Leak fix in parseFile. Morten Welinder September 1999. */ /* * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved. * * This file may be freely copied and redistributed as long as: * 1) This entire notice continues to be included in the file, * 2) If the file has been modified in any way, a notice of such * modification is conspicuously indicated. * * PostScript, Display PostScript, and Adobe are registered trademarks of * Adobe Systems Incorporated. * * ************************************************************************ * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. * ************************************************************************ */ /* parseAFM.c * * This file is used in conjuction with the parseAFM.h header file. * This file contains several procedures that are used to parse AFM * files. It is intended to work with an application program that needs * font metric information. The program can be used as is by making a * procedure call to "parseFile" (passing in the expected parameters) * and having it fill in a data structure with the data from the * AFM file, or an application developer may wish to customize this * code. * * There is also a file, parseAFMclient.c, that is a sample application * showing how to call the "parseFile" procedure and how to use the data * after "parseFile" has returned. * * Please read the comments in parseAFM.h and parseAFMclient.c. * * History: * original: DSM Thu Oct 20 17:39:59 PDT 1988 * modified: DSM Mon Jul 3 14:17:50 PDT 1989 * - added 'storageProblem' return code * - fixed bug of not allocating extra byte for string duplication * - fixed typos * modified: DSM Tue Apr 3 11:18:34 PDT 1990 * - added free(ident) at end of parseFile routine * modified: DSM Tue Jun 19 10:16:29 PDT 1990 * - changed (width == 250) to (width = 250) in initializeArray */ #include #include #include #if !defined(_WIN32) && !defined(macintosh) # include #endif #include #include #include "parseAFM.h" #define lineterm EOL /* line terminating character */ #define lineterm_alt '\r' /* alternative line terminating character */ #define normalEOF 1 /* return code from parsing routines used only */ /* in this module */ #define Space "space" /* used in string comparison to look for the width */ /* of the space character to init the widths array */ #define False "false" /* used in string comparison to check the value of */ /* boolean keys (e.g. IsFixedPitch) */ #define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0) /*************************** GLOBALS ***********************/ static char *ident = NULL; /* storage buffer for keywords */ /* "shorts" for fast case statement * The values of each of these enumerated items correspond to an entry in the * table of strings defined below. Therefore, if you add a new string as * new keyword into the keyStrings table, you must also add a corresponding * parseKey AND it MUST be in the same position! * * IMPORTANT: since the sorting algorithm is a binary search, the strings of * keywords must be placed in lexicographical order, below. [Therefore, the * enumerated items are not necessarily in lexicographical order, depending * on the name chosen. BUT, they must be placed in the same position as the * corresponding key string.] The NOPE shall remain in the last position, * since it does not correspond to any key string, and it is used in the * "recognize" procedure to calculate how many possible keys there are. */ enum parseKey { ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT, DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN, FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH, ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME, NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS, STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION, UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT, NOPE }; /* keywords for the system: * This a table of all of the current strings that are vaild AFM keys. * Each entry can be referenced by the appropriate parseKey value (an * enumerated data type defined above). If you add a new keyword here, * a corresponding parseKey MUST be added to the enumerated data type * defined above, AND it MUST be added in the same position as the * string is in this table. * * IMPORTANT: since the sorting algorithm is a binary search, the keywords * must be placed in lexicographical order. And, NULL should remain at the * end. */ static char *keyStrings[] = { "Ascender", "B", "C", "CC", "CapHeight", "Comment", "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites", "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern", "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch", "ItalicAngle", "KP", "KPX", "L", "N", "Notice", "PCC", "StartCharMetrics", "StartComposites", "StartFontMetrics", "StartKernData", "StartKernPairs", "StartTrackKern", "TrackKern", "UnderlinePosition", "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight", NULL }; /*************************** PARSING ROUTINES **************/ /*************************** token *************************/ /* A "AFM File Conventions" tokenizer. That means that it will * return the next token delimited by white space. See also * the `linetoken' routine, which does a similar thing but * reads all tokens until the next end-of-line. */ static char *token(FILE *stream) { int ch, idx; /* skip over white space */ while ((ch = fgetc(stream)) == ' ' || ch == lineterm || ch == lineterm_alt || ch == ',' || ch == '\t' || ch == ';'); idx = 0; while (idx < MAX_NAME - 1 && ch != EOF && ch != ' ' && ch != lineterm && ch != lineterm_alt && ch != '\t' && ch != ':' && ch != ';') { ident[idx++] = ch; ch = fgetc(stream); } /* while */ if (ch == EOF && idx < 1) return ((char *)NULL); if (idx >= 1 && ch != ':' ) ungetc(ch, stream); if (idx < 1 ) ident[idx++] = ch; /* single-character token */ ident[idx] = 0; return(ident); /* returns pointer to the token */ } /* token */ /*************************** linetoken *************************/ /* "linetoken" will get read all tokens until the EOL character from * the given stream. This is used to get any arguments that can be * more than one word (like Comment lines and FullName). */ static char *linetoken(FILE *stream) { int ch, idx; while ((ch = fgetc(stream)) == ' ' || ch == '\t' ); idx = 0; while (idx < MAX_NAME - 1 && ch != EOF && ch != lineterm && ch != lineterm_alt) { ident[idx++] = ch; ch = fgetc(stream); } /* while */ ungetc(ch, stream); ident[idx] = 0; return(ident); /* returns pointer to the token */ } /* linetoken */ /*************************** recognize *************************/ /* This function tries to match a string to a known list of * valid AFM entries (check the keyStrings array above). * "ident" contains everything from white space through the * next space, tab, or ":" character. * * The algorithm is a standard Knuth binary search. */ static enum parseKey recognize(register char *ident) { int lower = 0, upper = (int) NOPE, midpoint, cmpvalue; BOOL found = FALSE; while ((upper >= lower) && !found) { midpoint = (lower + upper)/2; if (keyStrings[midpoint] == NULL) break; cmpvalue = strncmp(ident, keyStrings[midpoint], MAX_NAME); if (cmpvalue == 0) found = TRUE; else if (cmpvalue < 0) upper = midpoint - 1; else lower = midpoint + 1; } /* while */ if (found) return (enum parseKey) midpoint; else return NOPE; } /* recognize */ /************************* parseGlobals *****************************/ /* This function is called by "parseFile". It will parse the AFM File * up to the "StartCharMetrics" keyword, which essentially marks the * end of the Global Font Information and the beginning of the character * metrics information. * * If the caller of "parseFile" specified that it wanted the Global * Font Information (as defined by the "AFM File Specification" * document), then that information will be stored in the returned * data structure. * * Any Global Font Information entries that are not found in a * given file, will have the usual default initialization value * for its type (i.e. entries of type int will be 0, etc). * * This function returns an error code specifying whether there was * a premature EOF or a parsing error. This return value is used by * parseFile to determine if there is more file to parse. */ static BOOL parseGlobals(FILE *fp, register GlobalFontInfo *gfi) { BOOL cont = TRUE, save = (gfi != NULL); int error = AFM_ok; register char *keyword; while (cont) { keyword = token(fp); if (keyword == NULL) /* Have reached an early and unexpected EOF. */ /* Set flag and stop parsing */ { error = AFM_earlyEOF; break; /* get out of loop */ } if (!save) /* get tokens until the end of the Global Font info section */ /* without saving any of the data */ switch (recognize(keyword)) { case STARTCHARMETRICS: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; default: break; } /* switch */ else /* otherwise parse entire global font info section, */ /* saving the data */ switch(recognize(keyword)) { case STARTFONTMETRICS: keyword = token(fp); gfi->afmVersion = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->afmVersion, keyword); break; case COMMENT: keyword = linetoken(fp); break; case FONTNAME: keyword = token(fp); gfi->fontName = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->fontName, keyword); break; case ENCODINGSCHEME: keyword = token(fp); gfi->encodingScheme = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->encodingScheme, keyword); break; case FULLNAME: keyword = linetoken(fp); gfi->fullName = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->fullName, keyword); break; case FAMILYNAME: keyword = linetoken(fp); gfi->familyName = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->familyName, keyword); break; case WEIGHT: keyword = token(fp); gfi->weight = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->weight, keyword); break; case ITALICANGLE: keyword = token(fp); gfi->italicAngle = (float)atof(keyword); if (errno == ERANGE) error = AFM_parseError; break; case ISFIXEDPITCH: keyword = token(fp); if (MATCH(keyword, False)) gfi->isFixedPitch = 0; else gfi->isFixedPitch = 1; break; case UNDERLINEPOSITION: keyword = token(fp); gfi->underlinePosition = atoi(keyword); break; case UNDERLINETHICKNESS: keyword = token(fp); gfi->underlineThickness = atoi(keyword); break; case VERSION: keyword = linetoken(fp); gfi->version = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->version, keyword); break; case NOTICE: keyword = linetoken(fp); gfi->notice = (char *) malloc(strlen(keyword) + 1); strcpy(gfi->notice, keyword); break; case FONTBBOX: keyword = token(fp); gfi->fontBBox.llx = atoi(keyword); keyword = token(fp); gfi->fontBBox.lly = atoi(keyword); keyword = token(fp); gfi->fontBBox.urx = atoi(keyword); keyword = token(fp); gfi->fontBBox.ury = atoi(keyword); break; case CAPHEIGHT: keyword = token(fp); gfi->capHeight = atoi(keyword); break; case XHEIGHT: keyword = token(fp); gfi->xHeight = atoi(keyword); break; case DESCENDER: keyword = token(fp); gfi->descender = atoi(keyword); break; case ASCENDER: keyword = token(fp); gfi->ascender = atoi(keyword); break; case STARTCHARMETRICS: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; case NOPE: default: error = AFM_parseError; break; } /* switch */ } /* while */ return(error); } /* parseGlobals */ #if 0 /************************* initializeArray ************************/ /* Unmapped character codes are (at Adobe Systems) assigned the * width of the space character (if one exists) else they get the * value of 250 ems. This function initializes all entries in the * char widths array to have this value. Then any mapped character * codes will be replaced with the width of the appropriate character * when parsing the character metric section. * This function parses the Character Metrics Section looking * for a space character (by comparing character names). If found, * the width of the space character will be used to initialize the * values in the array of character widths. * * Before returning, the position of the read/write pointer of the * file is reset to be where it was upon entering this function. */ static int initializeArray(FILE *fp, register int *cwi) { BOOL cont = TRUE, found = FALSE; long opos = ftell(fp); int code = 0, width = 0, i = 0, error = 0; register char *keyword; while (cont) { keyword = token(fp); if (keyword == NULL) { error = AFM_earlyEOF; break; /* get out of loop */ } switch(recognize(keyword)) { case COMMENT: keyword = linetoken(fp); break; case CODE: code = atoi(token(fp)); break; case XWIDTH: width = atoi(token(fp)); break; case CHARNAME: keyword = token(fp); if (MATCH(keyword, Space)) { cont = FALSE; found = TRUE; } break; case ENDCHARMETRICS: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; case NOPE: default: error = AFM_parseError; break; } /* switch */ } /* while */ if (!found) width = 250; for (i = 0; i < 256; ++i) cwi[i] = width; fseek(fp, opos, 0); return(error); } /* initializeArray */ #endif /************************* parseCharWidths **************************/ /* This function is called by "parseFile". It will parse the AFM File * up to the "EndCharMetrics" keyword. It will save the character * width info (as opposed to all of the character metric information) * if requested by the caller of parseFile. Otherwise, it will just * parse through the section without saving any information. * * If data is to be saved, parseCharWidths is passed in a pointer * to an array of widths that has already been initialized by the * standard value for unmapped character codes. This function parses * the Character Metrics section only storing the width information * for the encoded characters into the array using the character code * as the index into that array. * * This function returns an error code specifying whether there was * a premature EOF or a parsing error. This return value is used by * parseFile to determine if there is more file to parse. */ static int parseCharWidths(FILE *fp, register int *cwi) { BOOL cont = TRUE, save = (cwi != NULL); int pos = 0, error = AFM_ok; register char *keyword; while (cont) { keyword = token(fp); /* Have reached an early and unexpected EOF. */ /* Set flag and stop parsing */ if (keyword == NULL) { error = AFM_earlyEOF; break; /* get out of loop */ } if (!save) /* get tokens until the end of the Char Metrics section without */ /* saving any of the data*/ switch (recognize(keyword)) { case ENDCHARMETRICS: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; default: break; } /* switch */ else /* otherwise parse entire char metrics section, saving */ /* only the char x-width info */ switch(recognize(keyword)) { case COMMENT: keyword = linetoken(fp); break; case CODE: keyword = token(fp); pos = atoi(keyword); break; case XYWIDTH: /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */ keyword = token(fp); keyword = token(fp); /* eat values */ error = AFM_parseError; break; case XWIDTH: keyword = token(fp); if (pos >= 0) /* ignore unmapped chars */ cwi[pos] = atoi(keyword); break; case ENDCHARMETRICS: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; case CHARNAME: /* eat values (so doesn't cause AFM_parseError) */ keyword = token(fp); break; case CHARBBOX: keyword = token(fp); keyword = token(fp); keyword = token(fp); keyword = token(fp); break; case LIGATURE: keyword = token(fp); keyword = token(fp); break; case NOPE: default: error = AFM_parseError; break; } /* switch */ } /* while */ return(error); } /* parseCharWidths */ /************************* parseCharMetrics ************************/ /* This function is called by parseFile if the caller of parseFile * requested that all character metric information be saved * (as opposed to only the character width information). * * parseCharMetrics is passed in a pointer to an array of records * to hold information on a per character basis. This function * parses the Character Metrics section storing all character * metric information for the ALL characters (mapped and unmapped) * into the array. * * This function returns an error code specifying whether there was * a premature EOF or a parsing error. This return value is used by * parseFile to determine if there is more file to parse. */ static int parseCharMetrics(FILE *fp, register Font_Info *fi) { BOOL cont = TRUE, firstTime = TRUE; int error = AFM_ok, count = 0; register CharMetricInfo *temp = fi->cmi; register char *keyword; while (cont) { keyword = token(fp); if (keyword == NULL) { error = AFM_earlyEOF; break; /* get out of loop */ } switch(recognize(keyword)) { case COMMENT: keyword = linetoken(fp); break; case CODE: if (count < fi->numOfChars) { if (firstTime) firstTime = FALSE; else temp++; temp->code = atoi(token(fp)); count++; } else { error = AFM_parseError; cont = FALSE; } break; case XYWIDTH: temp->wx = atoi(token(fp)); temp->wy = atoi(token(fp)); break; case XWIDTH: temp->wx = atoi(token(fp)); break; case CHARNAME: keyword = token(fp); temp->name = (char *) malloc(strlen(keyword) + 1); strcpy(temp->name, keyword); break; case CHARBBOX: temp->charBBox.llx = atoi(token(fp)); temp->charBBox.lly = atoi(token(fp)); temp->charBBox.urx = atoi(token(fp)); temp->charBBox.ury = atoi(token(fp)); break; case LIGATURE: { Ligature **tail = &(temp->ligs); Ligature *node = *tail; if (*tail != NULL) { while (node->next != NULL) node = node->next; tail = &(node->next); } *tail = (Ligature *) calloc(1, sizeof(Ligature)); keyword = token(fp); (*tail)->succ = (char *) malloc(strlen(keyword) + 1); strcpy((*tail)->succ, keyword); keyword = token(fp); (*tail)->lig = (char *) malloc(strlen(keyword) + 1); strcpy((*tail)->lig, keyword); break; } case ENDCHARMETRICS: cont = FALSE;; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; case NOPE: default: error = AFM_parseError; break; } /* switch */ } /* while */ if ((error == AFM_ok) && (count != fi->numOfChars)) error = AFM_parseError; return(error); } /* parseCharMetrics */ /************************* parseTrackKernData ***********************/ /* This function is called by "parseFile". It will parse the AFM File * up to the "EndTrackKern" or "EndKernData" keywords. It will save the * track kerning data if requested by the caller of parseFile. * * parseTrackKernData is passed in a pointer to the FontInfo record. * If data is to be saved, the FontInfo record will already contain * a valid pointer to storage for the track kerning data. * * This function returns an error code specifying whether there was * a premature EOF or a parsing error. This return value is used by * parseFile to determine if there is more file to parse. */ static int parseTrackKernData(FILE *fp, register Font_Info *fi) { BOOL cont = TRUE, save = (fi->tkd != NULL); int pos = 0, error = AFM_ok, tcount = 0; register char *keyword; while (cont) { keyword = token(fp); if (keyword == NULL) { error = AFM_earlyEOF; break; /* get out of loop */ } if (!save) /* get tokens until the end of the Track Kerning Data */ /* section without saving any of the data */ switch(recognize(keyword)) { case ENDTRACKKERN: case ENDKERNDATA: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; default: break; } /* switch */ else /* otherwise parse entire Track Kerning Data section, */ /* saving the data */ switch(recognize(keyword)) { case COMMENT: keyword = linetoken(fp); break; case TRACKKERN: if (tcount < fi->numOfTracks) { keyword = token(fp); fi->tkd[pos].degree = atoi(keyword); keyword = token(fp); fi->tkd[pos].minPtSize = (float)atof(keyword); if (errno == ERANGE) error = AFM_parseError; keyword = token(fp); fi->tkd[pos].minKernAmt = (float)atof(keyword); if (errno == ERANGE) error = AFM_parseError; keyword = token(fp); fi->tkd[pos].maxPtSize = (float)atof(keyword); if (errno == ERANGE) error = AFM_parseError; keyword = token(fp); fi->tkd[pos++].maxKernAmt = (float)atof(keyword); if (errno == ERANGE) error = AFM_parseError; tcount++; } else { error = AFM_parseError; cont = FALSE; } break; case ENDTRACKKERN: case ENDKERNDATA: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; case NOPE: default: error = AFM_parseError; break; } /* switch */ } /* while */ if (error == AFM_ok && tcount != fi->numOfTracks) error = AFM_parseError; return(error); } /* parseTrackKernData */ /************************* parsePairKernData ************************/ /* This function is called by "parseFile". It will parse the AFM File * up to the "EndKernPairs" or "EndKernData" keywords. It will save * the pair kerning data if requested by the caller of parseFile. * * parsePairKernData is passed in a pointer to the FontInfo record. * If data is to be saved, the FontInfo record will already contain * a valid pointer to storage for the pair kerning data. * * This function returns an error code specifying whether there was * a premature EOF or a parsing error. This return value is used by * parseFile to determine if there is more file to parse. */ static int parsePairKernData(FILE *fp, register Font_Info *fi) { BOOL cont = TRUE, save = (fi->pkd != NULL); int pos = 0, error = AFM_ok, pcount = 0; register char *keyword; while (cont) { keyword = token(fp); if (keyword == NULL) { error = AFM_earlyEOF; break; /* get out of loop */ } if (!save) /* get tokens until the end of the Pair Kerning Data */ /* section without saving any of the data */ switch(recognize(keyword)) { case ENDKERNPAIRS: case ENDKERNDATA: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; default: break; } /* switch */ else /* otherwise parse entire Pair Kerning Data section, */ /* saving the data */ switch(recognize(keyword)) { case COMMENT: keyword = linetoken(fp); break; case KERNPAIR: if (pcount < fi->numOfPairs) { keyword = token(fp); fi->pkd[pos].name1 = (char *) malloc(strlen(keyword) + 1); strcpy(fi->pkd[pos].name1, keyword); keyword = token(fp); fi->pkd[pos].name2 = (char *) malloc(strlen(keyword) + 1); strcpy(fi->pkd[pos].name2, keyword); keyword = token(fp); fi->pkd[pos].xamt = atoi(keyword); keyword = token(fp); fi->pkd[pos++].yamt = atoi(keyword); pcount++; } else { error = AFM_parseError; cont = FALSE; } break; case KERNPAIRXAMT: if (pcount < fi->numOfPairs) { keyword = token(fp); fi->pkd[pos].name1 = (char *) malloc(strlen(keyword) + 1); strcpy(fi->pkd[pos].name1, keyword); keyword = token(fp); fi->pkd[pos].name2 = (char *) malloc(strlen(keyword) + 1); strcpy(fi->pkd[pos].name2, keyword); keyword = token(fp); fi->pkd[pos++].xamt = atoi(keyword); pcount++; } else { error = AFM_parseError; cont = FALSE; } break; case ENDKERNPAIRS: case ENDKERNDATA: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; case NOPE: default: error = AFM_parseError; break; } /* switch */ } /* while */ if (error == AFM_ok && pcount != fi->numOfPairs) error = AFM_parseError; return(error); } /* parsePairKernData */ /************************* parseCompCharData **************************/ /* This function is called by "parseFile". It will parse the AFM File * up to the "EndComposites" keyword. It will save the composite * character data if requested by the caller of parseFile. * * parseCompCharData is passed in a pointer to the FontInfo record, and * a boolean representing if the data should be saved. * * This function will create the appropriate amount of storage for * the composite character data and store a pointer to the storage * in the FontInfo record. * * This function returns an error code specifying whether there was * a premature EOF or a parsing error. This return value is used by * parseFile to determine if there is more file to parse. */ static int parseCompCharData(FILE *fp, register Font_Info *fi) { BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL); int pos = 0, j = 0, error = AFM_ok, ccount = 0, pcount = 0; register char *keyword; while (cont) { keyword = token(fp); if (keyword == NULL) /* Have reached an early and unexpected EOF. */ /* Set flag and stop parsing */ { error = AFM_earlyEOF; break; /* get out of loop */ } if (ccount > fi->numOfComps) { error = AFM_parseError; break; /* get out of loop */ } if (!save) /* get tokens until the end of the Composite Character info */ /* section without saving any of the data */ switch(recognize(keyword)) { case ENDCOMPOSITES: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; default: break; } /* switch */ else /* otherwise parse entire Composite Character info section, */ /* saving the data */ switch(recognize(keyword)) { case COMMENT: keyword = linetoken(fp); break; case COMPCHAR: if (ccount < fi->numOfComps) { keyword = token(fp); if (pcount != fi->ccd[pos].numOfPieces) error = AFM_parseError; pcount = 0; if (firstTime) firstTime = FALSE; else pos++; fi->ccd[pos].ccName = (char *) malloc(strlen(keyword) + 1); strcpy(fi->ccd[pos].ccName, keyword); keyword = token(fp); fi->ccd[pos].numOfPieces = atoi(keyword); fi->ccd[pos].pieces = (Pcc *) calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc)); j = 0; ccount++; } else { error = AFM_parseError; cont = FALSE; } break; case COMPCHARPIECE: if (pcount < fi->ccd[pos].numOfPieces) { keyword = token(fp); fi->ccd[pos].pieces[j].pccName = (char *) malloc(strlen(keyword) + 1); strcpy(fi->ccd[pos].pieces[j].pccName, keyword); keyword = token(fp); fi->ccd[pos].pieces[j].deltax = atoi(keyword); keyword = token(fp); fi->ccd[pos].pieces[j++].deltay = atoi(keyword); pcount++; } else error = AFM_parseError; break; case ENDCOMPOSITES: cont = FALSE; break; case ENDFONTMETRICS: cont = FALSE; error = normalEOF; break; case NOPE: default: error = AFM_parseError; break; } /* switch */ } /* while */ if (error == AFM_ok && ccount != fi->numOfComps) error = AFM_parseError; return(error); } /* parseCompCharData */ /*************************** 'PUBLIC' FUNCTION ********************/ /*************************** parseFile *****************************/ /* parseFile is the only 'public' procedure available. It is called * from an application wishing to get information from an AFM file. * The caller of this function is responsible for locating and opening * an AFM file and handling all errors associated with that task. * * parseFile expects 3 parameters: a vaild file pointer, a pointer * to a (FontInfo *) variable (for which storage will be allocated and * the data requested filled in), and a mask specifying which * data from the AFM File should be saved in the FontInfo structure. * * The file will be parsed and the requested data will be stored in * a record of type FontInfo (refer to ParseAFM.h). * * parseFile returns an error code as defined in parseAFM.h. * * The position of the read/write pointer associated with the file * pointer upon return of this function is undefined. */ extern int parseFile (FILE *fp, Font_Info **fi, FLAGS flags) { int code = AFM_ok; /* return code from each of the parsing routines */ int error = AFM_ok; /* used as the return code from this function */ register char *keyword; /* used to store a token */ /* storage data for the global variable ident */ if (!ident) ident = (char *) calloc(MAX_NAME, sizeof(char)); if (ident == NULL) {error = AFM_storageProblem; return(error);} (*fi) = (Font_Info *) calloc(1, sizeof(Font_Info)); if ((*fi) == NULL) {error = AFM_storageProblem; return(error);} if (flags & P_G) { (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo)); if ((*fi)->gfi == NULL) {error = AFM_storageProblem; return(error);} } /* The AFM File begins with Global Font Information. This section */ /* will be parsed whether or not information should be saved. */ code = parseGlobals(fp, (*fi)->gfi); if (code < 0) error = code; /* The Global Font Information is followed by the Character Metrics */ /* section. Which procedure is used to parse this section depends on */ /* how much information should be saved. If all of the metrics info */ /* is wanted, parseCharMetrics is called. If only the character widths */ /* is wanted, parseCharWidths is called. parseCharWidths will also */ /* be called in the case that no character data is to be saved, just */ /* to parse through the section. */ if ((code != normalEOF) && (code != AFM_earlyEOF)) { (*fi)->numOfChars = atoi(token(fp)); if (flags & (P_M ^ P_W)) { (*fi)->cmi = (CharMetricInfo *) calloc((*fi)->numOfChars, sizeof(CharMetricInfo)); if ((*fi)->cmi == NULL) {error = AFM_storageProblem; return(error);} code = parseCharMetrics(fp, *fi); } else { if (flags & P_W) { (*fi)->cwi = (int *) calloc(256, sizeof(int)); if ((*fi)->cwi == NULL) { error = AFM_storageProblem; return(error); } } /* parse section regardless */ code = parseCharWidths(fp, (*fi)->cwi); } /* else */ } /* if */ if ((error != AFM_earlyEOF) && (code < 0)) error = code; /* The remaining sections of the AFM are optional. This code will */ /* look at the next keyword in the file to determine what section */ /* is next, and then allocate the appropriate amount of storage */ /* for the data (if the data is to be saved) and call the */ /* appropriate parsing routine to parse the section. */ while ((code != normalEOF) && (code != AFM_earlyEOF)) { keyword = token(fp); if (keyword == NULL) /* Have reached an early and unexpected EOF. */ /* Set flag and stop parsing */ { code = AFM_earlyEOF; break; /* get out of loop */ } switch(recognize(keyword)) { case STARTKERNDATA: break; case ENDKERNDATA: break; case STARTTRACKKERN: keyword = token(fp); if (flags & P_T) { (*fi)->numOfTracks = atoi(keyword); (*fi)->tkd = (TrackKernData *) calloc((*fi)->numOfTracks, sizeof(TrackKernData)); if ((*fi)->tkd == NULL) { error = AFM_storageProblem; return(error); } } /* if */ code = parseTrackKernData(fp, *fi); break; case STARTKERNPAIRS: keyword = token(fp); if (flags & P_P) { (*fi)->numOfPairs = atoi(keyword); (*fi)->pkd = (PairKernData *) calloc((*fi)->numOfPairs, sizeof(PairKernData)); if ((*fi)->pkd == NULL) { error = AFM_storageProblem; return(error); } } /* if */ code = parsePairKernData(fp, *fi); break; case STARTCOMPOSITES: keyword = token(fp); if (flags & P_C) { (*fi)->numOfComps = atoi(keyword); (*fi)->ccd = (CompCharData *) calloc((*fi)->numOfComps, sizeof(CompCharData)); if ((*fi)->ccd == NULL) { error = AFM_storageProblem; return(error); } } /* if */ code = parseCompCharData(fp, *fi); break; case ENDFONTMETRICS: code = normalEOF; break; case NOPE: default: code = AFM_parseError; break; } /* switch */ if ((error != AFM_earlyEOF) && (code < 0)) error = code; } /* while */ if ((error != AFM_earlyEOF) && (code < 0)) error = code; if (ident != NULL) { free(ident); ident = NULL; } return(error); } /* parseFile */ void parseFileFree (Font_Info *fi) { if (fi->gfi) { free (fi->gfi->afmVersion); free (fi->gfi->fontName); free (fi->gfi->fullName); free (fi->gfi->familyName); free (fi->gfi->weight); free (fi->gfi->version); free (fi->gfi->notice); free (fi->gfi->encodingScheme); free (fi->gfi); } /* This contains just scalars. */ free (fi->cwi); if (fi->cmi) { int i; for (i = 0; i < fi->numOfChars; i++) { Ligature *ligs; free (fi->cmi[i].name); ligs = fi->cmi[i].ligs; while (ligs) { Ligature *tmp; tmp = ligs; ligs = ligs->next; free (tmp->succ); free (tmp->lig); free (tmp); } } free (fi->cmi); } /* This contains just scalars. */ free (fi->tkd); if (fi->pkd) { int i; for (i = 0; i < fi->numOfPairs; i++) { free (fi->pkd[i].name1); free (fi->pkd[i].name2); } free (fi->pkd); } if (fi->ccd) { int i, j; for (i = 0; i < fi->numOfComps; i++) { free (fi->ccd[i].ccName); for (j = 0; j < fi->ccd[i].numOfPieces; j++) { free (fi->ccd[i].pieces[j].pccName); } free (fi->ccd[i].pieces); } free (fi->ccd); } free (fi); } rl-renderpm-4.0.3/src/gt1/parseAFM.h000066400000000000000000000266101453236046100170470ustar00rootroot00000000000000/* Modified 1999 Morten Welinder: * 1. ANSI prototype. * 2. parseFileFree function. */ /* * (C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved. * * This file may be freely copied and redistributed as long as: * 1) This entire notice continues to be included in the file, * 2) If the file has been modified in any way, a notice of such * modification is conspicuously indicated. * * PostScript, Display PostScript, and Adobe are registered trademarks of * Adobe Systems Incorporated. * * ************************************************************************ * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. * ************************************************************************ */ /* ParseAFM.h * * This header file is used in conjuction with the parseAFM.c file. * Together these files provide the functionality to parse Adobe Font * Metrics files and store the information in predefined data structures. * It is intended to work with an application program that needs font metric * information. The program can be used as is by making a procedure call to * parse an AFM file and have the data stored, or an application developer * may wish to customize the code. * * This header file defines the data structures used as well as the key * strings that are currently recognized by this version of the AFM parser. * This program is based on the document "Adobe Font Metrics Files, * Specification Version 2.0". * * AFM files are separated into distinct sections of different data. Because * of this, the parseAFM program can parse a specified file to only save * certain sections of information based on the application's needs. A record * containing the requested information will be returned to the application. * * AFM files are divided into five sections of data: * 1) The Global Font Information * 2) The Character Metrics Information * 3) The Track Kerning Data * 4) The Pair-Wise Kerning Data * 5) The Composite Character Data * * Basically, the application can request any of these sections independent * of what other sections are requested. In addition, in recognizing that * many applications will want ONLY the x-width of characters and not all * of the other character metrics information, there is a way to receive * only the width information so as not to pay the storage cost for the * unwanted data. An application should never request both the * "quick and dirty" char metrics (widths only) and the Character Metrics * Information since the Character Metrics Information will contain all * of the character widths as well. * * There is a procedure in parseAFM.c, called parseFile, that can be * called from any application wishing to get information from the AFM File. * This procedure expects 3 parameters: a vaild file descriptor, a pointer * to a (FontInfo *) variable (for which space will be allocated and then * will be filled in with the data requested), and a mask specifying * which data from the AFM File should be saved in the FontInfo structure. * * The flags that can be used to set the appropriate mask are defined below. * In addition, several commonly used masks have already been defined. * * History: * original: DSM Thu Oct 20 17:39:59 PDT 1988 * modified: DSM Mon Jul 3 14:17:50 PDT 1989 * - added 'storageProblem' return code * - fixed typos */ #include /* your basic constants */ #undef TRUE #define TRUE 1 #undef FALSE #define FALSE 0 #define EOL '\n' /* end-of-line indicator */ #define MAX_NAME 4096 /* max length for identifiers */ #define BOOL int #define FLAGS int /* Flags that can be AND'ed together to specify exactly what * information from the AFM file should be saved. */ #define P_G 0x01 /* 0000 0001 */ /* Global Font Info */ #define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */ #define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */ #define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */ #define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */ #define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */ /* Commonly used flags */ #define P_GW (P_G | P_W) #define P_GM (P_G | P_M) #define P_GMP (P_G | P_M | P_P) #define P_GMK (P_G | P_M | P_P | P_T) #define P_GALL (P_G | P_M | P_P | P_T | P_C) /* Possible return codes from the parseFile procedure. * * ok means there were no problems parsing the file. * * parseError means that there was some kind of parsing error, but the * parser went on. This could include problems like the count for any given * section does not add up to how many entries there actually were, or * there was a key that was not recognized. The return record may contain * vaild data or it may not. * * earlyEOF means that an End of File was encountered before expected. This * may mean that the AFM file had been truncated, or improperly formed. * * storageProblem means that there were problems allocating storage for * the data structures that would have contained the AFM data. */ #define AFM_ok 0 #define AFM_parseError -1 #define AFM_earlyEOF -2 #define AFM_storageProblem -3 /************************* TYPES *********************************/ /* Below are all of the data structure definitions. These structures * try to map as closely as possible to grouping and naming of data * in the AFM Files. */ /* Bounding box definition. Used for the Font BBox as well as the * Character BBox. */ typedef struct { int llx; /* lower left x-position */ int lly; /* lower left y-position */ int urx; /* upper right x-position */ int ury; /* upper right y-position */ } BBox; /* Global Font information. * The key that each field is associated with is in comments. For an * explanation about each key and its value please refer to the AFM * documentation (full title & version given above). */ typedef struct { char *afmVersion; /* key: StartFontMetrics */ char *fontName; /* key: FontName */ char *fullName; /* key: FullName */ char *familyName; /* key: FamilyName */ char *weight; /* key: Weight */ float italicAngle; /* key: ItalicAngle */ BOOL isFixedPitch; /* key: IsFixedPitch */ BBox fontBBox; /* key: FontBBox */ int underlinePosition; /* key: UnderlinePosition */ int underlineThickness; /* key: UnderlineThickness */ char *version; /* key: Version */ char *notice; /* key: Notice */ char *encodingScheme; /* key: EncodingScheme */ int capHeight; /* key: CapHeight */ int xHeight; /* key: XHeight */ int ascender; /* key: Ascender */ int descender; /* key: Descender */ } GlobalFontInfo; /* Ligature definition is a linked list since any character can have * any number of ligatures. */ typedef struct _t_ligature { char *succ, *lig; struct _t_ligature *next; } Ligature; /* Character Metric Information. This structure is used only if ALL * character metric information is requested. If only the character * widths is requested, then only an array of the character x-widths * is returned. * * The key that each field is associated with is in comments. For an * explanation about each key and its value please refer to the * Character Metrics section of the AFM documentation (full title * & version given above). */ typedef struct { int code, /* key: C */ wx, /* key: WX */ wy; /* together wx and wy are associated with key: W */ char *name; /* key: N */ BBox charBBox; /* key: B */ Ligature *ligs; /* key: L (linked list; not a fixed number of Ls */ } CharMetricInfo; /* Track kerning data structure. * The fields of this record are the five values associated with every * TrackKern entry. * * For an explanation about each value please refer to the * Track Kerning section of the AFM documentation (full title * & version given above). */ typedef struct { int degree; float minPtSize, minKernAmt, maxPtSize, maxKernAmt; } TrackKernData; /* Pair Kerning data structure. * The fields of this record are the four values associated with every * KP entry. For KPX entries, the yamt will be zero. * * For an explanation about each value please refer to the * Pair Kerning section of the AFM documentation (full title * & version given above). */ typedef struct { char *name1; char *name2; int xamt, yamt; } PairKernData; /* PCC is a piece of a composite character. This is a sub structure of a * compCharData described below. * These fields will be filled in with the values from the key PCC. * * For an explanation about each key and its value please refer to the * Composite Character section of the AFM documentation (full title * & version given above). */ typedef struct { char *pccName; int deltax, deltay; } Pcc; /* Composite Character Information data structure. * The fields ccName and numOfPieces are filled with the values associated * with the key CC. The field pieces points to an array (size = numOfPieces) * of information about each of the parts of the composite character. That * array is filled in with the values from the key PCC. * * For an explanation about each key and its value please refer to the * Composite Character section of the AFM documentation (full title * & version given above). */ typedef struct { char *ccName; int numOfPieces; Pcc *pieces; } CompCharData; /* FontInfo * Record type containing pointers to all of the other data * structures containing information about a font. * A a record of this type is filled with data by the * parseFile function. */ typedef struct { GlobalFontInfo *gfi; /* ptr to a GlobalFontInfo record */ int *cwi; /* ptr to 256 element array of just char widths */ int numOfChars; /* number of entries in char metrics array */ CharMetricInfo *cmi; /* ptr to char metrics array */ int numOfTracks; /* number to entries in track kerning array */ TrackKernData *tkd; /* ptr to track kerning array */ int numOfPairs; /* number to entries in pair kerning array */ PairKernData *pkd; /* ptr to pair kerning array */ int numOfComps; /* number to entries in comp char array */ CompCharData *ccd; /* ptr to comp char array */ } Font_Info; /************************* PROCEDURES ****************************/ /* Call this procedure to do the grunt work of parsing an AFM file. * * "fp" should be a valid file pointer to an AFM file. * * "fi" is a pointer to a pointer to a FontInfo record sturcture * (defined above). Storage for the FontInfo structure will be * allocated in parseFile and the structure will be filled in * with the requested data from the AFM File. * * "flags" is a mask with bits set representing what data should * be saved. Defined above are valid flags that can be used to set * the mask, as well as a few commonly used masks. * * The possible return codes from parseFile are defined above. */ int parseFile (FILE *fp, Font_Info **fi, FLAGS flags); void parseFileFree (Font_Info *fi); rl-renderpm-4.0.3/src/libart_lgpl/000077500000000000000000000000001453236046100170335ustar00rootroot00000000000000rl-renderpm-4.0.3/src/libart_lgpl/AUTHORS000066400000000000000000000000331453236046100200770ustar00rootroot00000000000000Raph Levien rl-renderpm-4.0.3/src/libart_lgpl/COPYING000066400000000000000000000613141453236046100200730ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! rl-renderpm-4.0.3/src/libart_lgpl/ChangeLog000066400000000000000000001054501453236046100206120ustar00rootroot000000000000002003-05-05 Alexander Larsson * configure.in: Bump to 2.3.12 2003-04-24 Alexander Larsson * art_uta_vpath.c (art_uta_from_vpath): Don't silently stomp on memory on bad vpaths. 2003-04-11 Alexander Larsson * art_svp_vpath_stroke.c (render_seg): Handle cases when dmr2 is very small better. 2003-04-10 Alexander Larsson * art_svp_wind.c (x_order_2): Handle horizontally aligned segments. 2002-11-25 Alexander Larsson * configure.in: Bump to 2.3.11 2002-10-16 John Harper * art_render_svp.c (art_render_svp_callback, art_render_svp_callback_span, art_render_svp_callback_opacity, art_render_svp_callback_opacity_span): if no runs would normally be emitted, but start is greater than zero, emit a single run covering the entire width of the rendered region 2002-08-18 Havoc Pennington * autogen.sh: hardcode aclocal-1.4/automake-1.4 so that users with both automake 1.6 and 1.4 installed get the right automake. Means compilation from CVS will now require the latest automake 1.4 release, or manually creating symlinks called "automake-1.4" and "aclocal-1.4" 2002-07-01 Alexander Larsson * Release 2.3.10 2002-07-01 Alexander Larsson * configure.in (LIBART_VERSION_INFO): Bump to 2.3.10 * art_svp_intersect.c (art_svp_intersect_add_seg): Initialize seg->wind_left to zero, this avoids uninitialized memory read later in art_svp_intersect_horiz_commit. 2002-06-24 Alexander Larsson Release 2.3.9. 2002-06-24 Alexander Larsson * configure.in: Bump to 2.3.9 2002-06-04 Balamurali Viswanathan * Makefile.am: Added -lm to libart_lgpl_2_la_LIBADD fixes bug 75711 (Jacob's suggestion) 2002-05-08 Raph Levien * art_uta_vpath.c (art_uta_add_line): Fixes very subtle edge case found by Federico Mena Quintero: (96, 96) - (96.220200017562348, 93.034868598919431). Previously, through numerical error, xn was a hair to the left, throwing off the Bresenham iteration. 2002-03-08 Tor Lillqvist * configure.in: Minor changes for build on Win32. Call AC_LIBTOOL_WIN32_DLL. Set automake conditionals OS_WIN32 and MS_LIB_AVAILABLE. * art_affine.c: Include art_misc.h for M_PI, which not necessarily is in math.h. * Makefile.am: On Win32, build and install import libraries, both for gcc and for MSVC (if available). * libart-zip.in: New file. Used to build runtime and developer package for Win32. 2002-02-06 Laszlo Peter * configure.in: add AC_FUNC_ALLOCA * art_render_gradient.c: copy alloca hacks from glib/galloca.h 2002-02-02 Alexander Larsson * art_render.c: Add special case for art_render_composite_8 for two cases that are very common when rendering SVGs with librsvg. I'm sure these can be optimized further, but i'm feeling very slow today. 2002-02-01 Alex Larsson * art_render_gradient.c: Add optimized case for depth==8, n_channels == 3 * test_gradient.c: Test case for gradients. Uses gtk+, so not built by default. 2002-01-10 Anders Carlsson * Release 2.3.8 2002-01-04 Anders Carlsson * configure.in: Bump version to 2.3.8. 2002-01-03 Darin Adler * Makefile.am: Add art_render_mask.[ch]. * art_bpath.h: * art_rect_svp.h: * art_rect_uta.h: * art_render.h: * art_render_gradient.h: * art_render_mask.h: * art_render_svp.h: * art_rgb_svp.h: * art_svp_intersect.h: * art_svp_ops.h: * art_svp_point.h: * art_svp_render_aa.h: * art_svp_vpath.h: * art_svp_vpath_stroke.h: * art_svp_wind.h: * art_uta.h: * art_uta_rect.h: * art_uta_ops.h: * art_uta_svp.h: * art_uta_vpath.h: * art_vpath_bpath.h: * art_vpath_dash.h: * art_vpath_svp.h: Fix includes so that each header includes what it needs. * art_affine.c: * art_alphagamma.c: * art_bpath.c: * art_gray_svp.c: * art_misc.c: * art_pixbuf.c: * art_rect.c: * art_rect_svp.c: * art_rect_uta.c: * art_render.c: * art_render_gradient.c: * art_render_mask.c: * art_render_svp.c: * art_rgb.c: * art_rgb_a_affine.c: * art_rgb_affine.c: * art_rgb_affine_private.c: * art_rgb_bitmap_affine.c: * art_rgb_pixbuf_affine.c: * art_rgb_rgba_affine.c: * art_rgb_svp.c: * art_rgba.c: * art_svp.c: * art_svp_intersect.c: * art_svp_ops.c: * art_svp_point.c: * art_svp_render_aa.c: * art_svp_vpath.c: * art_svp_vpath_stroke.c: * art_svp_wind.c: * art_uta.c: * art_uta_ops.c: * art_uta_rect.c: * art_uta_svp.c: * art_uta_vpath.c: * art_vpath.c: * art_vpath_bpath.c: * art_vpath_dash.c: * art_vpath_svp.c: Fix order of includes so that the corresponding header is included first, to test that each header includes what it needs. 2002-01-02 Darin Adler * art_alphagamma.h: * art_gray_svp.h: * art_pixbuf.h: * art_render.h: * art_rgb.h: * art_rgba.h: Add some missing includes. 2001-12-26 Raph Levien * art_svp_intersect.c: More fixes to intersector. When tops coincide, full ordering test is performed (with breaking), rather than just comparing b from line eq. Also, had a test for x >= backwards in art_svp_intersect_add_point. Thanks to Bruce Q. Hammond for test cases. 2001-11-21 Raph Levien * art_svp_intersect.c (art_svp_intersect_break): Changed break logic systematically so that new breaks are always in order, rather than allowing them to become out of order, then try to fix them up later. It's no doubt possible to come up with cases in which this reduces precision, but it simplifies life nicely, so I did it. * (art_svp_intersect_horiz): Initialize (a, b, c) values of hs to avoid UMR's when they are tested later. * art_svp_ops.c (print_ps_vpath): Changed the coordinate transform so that it's done in the PostScript rather than the C code that outputs the coordinates. This makes it easier to reconstruct the vector path from the debug output. 2001-11-16 Alex Larsson * art_rect.c (art_drect_svp_union, art_drect_svp): Don't call art_drect_union() in these functions, since it considers zero-width or zero-height svg segments to be empty. This causes it to think i.e. rectangular svps are empty. 2001-11-07 Raph Levien * art_svp_intersect.c (art_svp_intersect_horiz): Fixed important logic bug, failing to test crossings of neighbors when inserting horizontal lines. Also changed printf to art_dprint. * art_misc.c (art_dprint): Added function for debug printing, so verbose intersector output doesn't have to go through printf. * art_misc.h (ART_USE_NEW_INTERSECTOR): I've turned this on now, as the new intersector certainly seems to be performing better than the old one now. 2001-10-31 Anders Carlsson * Release 2.3.7 2001-10-15 Raph Levien * art_svp_intersect.c (art_svp_intersect_horiz): Minor logic fix so that horiz segments successfully cross zero length segments in the active list. (art_svp_intersect_test_cross): Flags indicating whether to do add_point (potentially breaking neighbors) to left and to right. (art_svp_intersect_insert_cross): Provide ART_BREAK_LEFT and ART_BREAK_RIGHT flags to art_svp_intersect_test_cross, depending on direction of search. (art_svp_intersect_advance_cursor): Provide flags (allow both left and right breaking) to test_cross. 2001-10-15 Raph Levien * art_svp_intersect.c (CHEAP_SANITYCHECK): Added an inexpensive sanitycheck to detect multiple insertions of a segment into the horiz list. (art_svp_writer_rewind_add_point): Avoid breaking lines below their bottom point. (art_svp_intersect_test_cross): Handle cases correctly where intersection point matches y0 of left or right segment. These _do_ happen in real world examples. Also, do add_point on newly inserted intersection point. 2001-10-14 Raph Levien * art_svp_intersect.c (art_svp_intersect_add_point): Fixed rather subtle logic bug that misplaced insertion point when seg argument was NULL. 2001-10-11 Raph Levien * art_svp_render_aa.c (art_svp_render_aa_iter_step): Got rid of qsort of steps, and now keep the step list in sorted order. Further, we combine duplicate steps with the same x value, which bounds the size of the step list to x1 - x0, so we don't need to dynamically resize it. Thanks greatly to Bruce Q. Hammond for the original version of this patch. 2001-10-09 Raph Levien * art_svp_intersect.c (art_svp_intersect_test_cross): Breaks bottom part of line segments in "too close" cases. 2001-10-09 Raph Levien * art_svp_intersect.c (art_svp_writer_rewind_add_point): Fixed bbox computation. (art_svp_intersector): Handle degenerate case where input SVP has 0 segments. * art_svp_intersect.h: Moved definition of art_svp_intersector inside #ifdef __cplusplus, so it links properly against C++ 2001-10-09 Raph Levien * art_svp_intersect.c (art_svp_intersect_break): Handle case when break y equals sweep line correctly. Also adds first try at winding number sanitychecker, but that makes too many false positives. 2001-10-07 Raph Levien * art_svp.c (EPSILON): Set to zero if new intersector is in use - we want svp's to be in strict sorted order. * art_svp_intersect.c (art_svp_intersect_test_cross): Explicitly check that top points are equal, and swap immediately if b is out of order. (art_svp_intersect_horiz): Break segments that intersect horizontal lines. Now passes "two squares with offset" test. 2001-10-05 Raph Levien * art_svp_intersect.c: Major changes to accommodate horizontal lines. Intersections of horizontal lines aren't fully processed, but should work a lot better than before. * testart.c: Minor tweaks. testpat now frees memory so it can be run under memprof to detect leaks. 2001-10-03 Raph Levien * art_svp_intersect.c (art_svp_intersect_advance_cursor): Made test_cross for inserted segments common between intersection processing and cursor advance, and also took care of a case that hadn't been handled before. Also added invariant sanitychecker for debugging purposes. 2001-10-02 Raph Levien * art_svp_ops.c: ART_USE_NEW_INTERSECTOR variants of svp ops changed to do shallow free of merged svp. 2001-10-01 Raph Levien * art_svp_intersect.c: * art_svp_intersect.h: * Makefile.am: * art_misc.h: * art_svp_wind.h: First commit of new intersector code. It is turned off by default, but can be enabled by #defining ART_USE_NEW_INTERSECTOR in art_misc.h. * art_svp_ops.c: Make svp ops use new intersector if enabled. * art_svp_vpath_stroke.c: Make vpath stroking use new intersector if enabled. * testart.c: New test case for intersector. Wed Sep 26 03:48:13 2001 George Lebl * Release 2.3.6 Wed Sep 26 03:11:40 2001 George Lebl * gen_art_config.c: Fix 64bit issues, cast sizeof return when using %d to print it. 2001-09-13 Havoc Pennington * Makefile.am: rename library to libart_lgpl_2 * libart-2.0.pc.in (Cflags): move includes * libart-config.in: move includes * Makefile.am: delete libartConf.sh, rename libart-config (EXTRA_DIST): don't install m4 files (libart_lgplincdir): move headers 2001-08-03 Michael Meeks * Version 2.3.5 for the API freeze. 2001-07-12 Darin Adler * art_affine.c: (art_affine_expansion): Fix handling of negative numbers. We ran into this bug a while back when figuring out why librsvg couldn't handle certain svg files. 2001-07-12 Darin Adler * art_misc.h: Change art_expand macro so it's a single statement, using the do while (0) trick, which gets rid of some warnings. * art_pixbuf.c: Add a missing include. * art_vpath_svp.c: (art_vpath_from_svp): Initialize a variable to avoid a compiler warning. * gen_art_config.c: Add a missing include. 2001-03-24 Martin Baulig Applied the patch from Alexander Larsson which was sitting in gnome-libs/patches/libart.diff since February. [FIXME: Alex, can you please provide a ChangeLog?]) * art_rgb_a_affine.[ch]: New files. 2001-01-07 Hans Breuer * art_misc.c : embryonic change to use libart_lgpl on win32 * libart.def : new file, exported functions for win32 dll * makefile.msc : handwritten for MSVC compiler 2000-09-30 Martin Baulig * libart-2.0.pc.in: Provide pkg-config script. * configure.in: Create libart-2.0.pc from libart-2.0.pc.in. * Makefile.am (pkgconfig_DATA): Install the libart-2.0.pc script in `$(libdir)/pkgconfig'. 2000-08-15 Raph Levien * art_render.c (art_render_image_solid_negotiate): Only sets ART_IMAGE_SOURCE_CAN_COMPOSITE when a compositing callback is selected. Previously was causing segfaults on non-alpha images. Thanks to Leonard Rosenthol for spotting the bug. Fri Jun 30 22:56:58 2000 Raph Levien * art_render.c (art_render_composite): Fixed a bug that caused it to ignore the alpha setting. Also art_render_composite_8(). 2000-06-01 John Sullivan * art_svp_render_aa.c: (art_svp_render_aa_iter_step): Made it build by correcting struct member name from Raph's previous checkin. Wed May 31 11:10:58 2000 Raph Levien * art_svp_render_aa.c (art_svp_render_aa_iter_step): Updated n_steps_max in iter structure after steps reallocation. Tue May 30 10:33:13 2000 Raph Levien * art_svp_render_aa.c (art_svp_render_aa_iter_step): Fixed not updating iter->steps when steps gets reallocated. 2000-05-30 Pavel Cisler * art_rgba.c: Make it build -- fix a broken include. Tue May 30 00:09:21 2000 Raph Levien * art_render_gradient.c (art_render_gradient_setpix): Fixed an off-by-one loop error. Mon May 29 15:00:39 2000 Raph Levien * Makefile.am: Moved relevant .h files into HEADERS stanza. Mon May 29 13:48:49 2000 Raph Levien This is a fairly major commit, as it adds both the new, modular rendering architecture and gradients. Quite a bit of the code feels like "reference code" now, in that it is (hopefully) correct, but not necessarily very fast. In addition, there remain a few things not done, including the use of SVP's as non-driver mask sources. AlphaGamma and filter level also remain unimplemented. No compositing modes other than ART_NORMAL are implemented. All in good time! * configure.in: added -Wmissing-prototypes flag. Bumped version number to 2.3.0. * art_render.h: * art_render.c: Added new rendering architecture. * art_render_svp.h: * art_render_svp.c: Added renderers to use SVP's as mask sources in new rendering architecture. * art_render_gradient.h: * art_render_gradient.c: Added linear and radial gradients as image sources in new rendering architecture. * art_rgba.h: * art_rgba.c: Added functions for manipulating and compositing RGBA pixels. * art_svp_wind.c: Added static to trap_epsilon(), #ifdef'd out traverse(). * art_uta_ops.c: Added #include "art_uta_ops.h". * art_uta_rect.c: Added #include "art_uta_rect.h". * art_uta_svp.h: fixed __ART_UTA_SVP_H__ name. * art_misc.h: Added ART_GNUC_NORETURN attribute, added that to the prototype for art_die(). Added "static" to function declarations to avoid warnings when compiled with * testart.c: Added gradient test. Thu May 25 23:30:39 2000 Raph Levien * art_svp_render_aa.h: * art_svp_render_aa.c: Added art_svp_render_aa_iter functions, suitable for iterative rendering of an svp, one scan line at a time. * configure.in: Bumped version to 2.2.0. Tue May 16 15:03:35 2000 Raph Levien * art_rgb_pixbuf_affine.c: Included corresponding .h file. * art_rgb_pixbuf_affine.h: Put recursive #includes inside LIBART_COMPILATION test. * art_gray_svp.c: * art_rgb_svp.c: Explicit casts for callback data. Also removed "render the steps into tmpbuf" comment. * gen_art_config.c: * Makefile.am: * configure.in: Added code to automatically generate an art_config.h file, to be installed in libart's include dir. This file defines ART_SIZEOF_{CHAR,SHORT,INT,LONG} and art_u{8,16,32}. * art_misc.h: Moved definition of art_u8 and art_u32 into art_config.h. Added GCC printf format attributes. * art_svp_wind.c (traverse): Fixed UMR bug here. The function isn't actually used, so it's just for cleanliness. 2000-04-18 Lauris Kaplinski * art_affine.c (art_affine_to_string): Replaced snprinf with art_ftoa to avoid localisation of generated numbers 2000-04-18 ERDI Gergo * art_rgb_pixbuf_affine.h: Included the ArtPixBuf declaration Fri Apr 14 16:33:55 2000 Raph Levien * art_svp_wind.c (art_svp_uncross, art_svp_rewind_uncrossed): Fixed uninitialized memory reads when inserting new segment into active_segs. * art_bpath.c (art_bpath_affine_transform): Made it avoid potential uninitialized memory reads when not all the coordinates are needed. Thanks to Morten Welinder for spotting both of these problems. 2000-04-05 Raph Levien * art_svp_wind.c: Make "colinear" warnings go to stderr instead of stdout. Of course, when I finish the new intersector, these will go away entirely. 2000-04-04 Raph Levien * art_uta_vpath.c (art_uta_add_line): Fixed bug that was causing segfaults on alphas. Thanks to msw for localizing it. 2000-01-17 Raph Levien * art_svp_vpath_stroke.c (art_svp_vpath_stroke): Typo in api header (thanks rak). 2000-01-16 Timur Bakeyev * autoconf.sh: Instead of jumping between srdir and builddir just process all the auto* staff in srcdir. In fact, just saying the same things in other words. 2000-01-10 Elliot Lee * Makefile.am, *.h: Add rather bad hacks to the header files to allow compilation * Makefile.am: Distribute libart-config.in 2000-01-09 Raph Levien art_rgb_pixbuf_affine.c, art_rgb_rgba_affine.c, art_rgb_svp.c, art_svp.c, art_svp_ops.c, art_svp_point.c, art_svp_render_aa.c, art_svp_vpath.c, art_svp_vpath_stroke.c, art_svp_wind.c, art_uta.c, art_uta_ops.c, art_uta_rect.c, art_uta_svp.c, art_uta_vpath.c, art_vpath.c, art_vpath_bpath.c, art_vpath_dash.c, art_vpath_svp.c: Added API documentation. Fri Sep 24 17:53:21 1999 Raph Levien * art_svp_render_aa.c (art_svp_render_insert_active): Avoid reading undefined memory (thanks to Morten Welinder). 1999-09-19 Raph Levien * art_pixbuf.c (art_pixbuf_duplicate): Added a duplicate function at the request of Michael Meeks. 1999-09-11 Raph Levien * art_affine.c (art_affine_to_string): Tightened the predicate for matching rotate-only affines, which was too weak. Thanks to lewing for spotting it! 1999-09-01 Raph Levien * art_affine.c, art_alphagamma.c, art_bpath.c, art_gray_svp.c, art_misc.c, art_pixbuf.c, art_rect.c, art_rect_svp.c, art_rect_uta.c, art_rgb.c, art_rgb_affine.c, art_rgb_bitmap_affine.c: Updates to api doc headers. 1999-08-24 Raph Levien * art_affine.c, art_alphagamma.c, art_alphagamma.h, art_bpath.c, art_bpath.h, art_gray_svp.c, art_misc.c, art_pixbuf.c, art_pixbuf.h, art_point.h, art_rect.c, art_rect.h: Added api documentation headers. * testart.c: Added "dash" test, for testing the vpath_dash functions. * art_rgb_pixbuf_affine.h: Fixed the #ifdef for conditional inclusion. Thanks to Kristian Hogsberg Kristensen for spotting the bug. 1999-08-24 Raph Levien * art_svp_render_aa.c (art_svp_render_aa): Added some tests to avoid NaN for infinite slopes, which were causing problems on Alphas. Closes bug #1966. 1999-08-20 Federico Mena Quintero * configure.in: Fixed library's libtool version number. 1999-08-03 Larry Ewing * art_vpath_dash.c (art_vpath_dash): fix a bug/typo that was causing certain paths to loop infinitely. 1999-07-28 Raph Levien * art_vpath_dash.[ch]: Added a function to add a dash style to vpaths. It is tested, but has a couple of rough edges (see code for details). * testart.c: added tests for the new vpath_dash functionality. * Makefile.am: added art_vpath_dash.[ch] files. 1999-07-26 Raph Levien * art_rgb.c (art_rgb_fill_run): fixed incorrect test for big-endianness. Thanks to Michael Zucchi for spotting it. Fri Jul 16 23:42:59 1999 Tim Janik * art_affine.c (art_affine_flip): flip translation matrixes as well, by inverting matrix[4] if (horz) and inverting matrix[5] if (vert). Fri Jul 16 23:03:26 1999 Tim Janik * art_pixbuf.[hc]: deprecated art_pixbuf_free_shallow(), people should always free pixbufs with art_pixbuf_free() and use the _dnotify variants for specific destruction behaviour. added art_pixbuf_new_rgb_dnotify() and art_pixbuf_new_rgba_dnotify() which allow for a destruction notification function. (this involved adding two extra pointers to the ArtPixBuf structure, and removal of the recently introduced int flags field). Mon Jul 12 01:13:23 1999 Tim Janik * art_affine.[hc]: added art_affine_equal(), which checks two matrixes for equality within grid alignment. Fri Jul 9 17:50:19 1999 Tim Janik * art_affine.[hc]: added art_affine_flip() to flip a matrix horizontally and/or vertically, or just copy it. added art_affine_shear() to setup a shearing matrix. Tue Jul 6 19:03:39 1999 Tim Janik * art_pixbuf.h: added an int flags; member to the end of the structure, it currently only holds information about whether the pixels member should be freed. (raph: i think flags is more generic than free_pixels, so we can reuse that field if further demands popup in the future). * art_pixbuf.c: (art_pixbuf_new_const_rgba): (art_pixbuf_new_const_rgb): new functions that prevent the pixels member from being freed upon art_pixbuf_free (). (art_pixbuf_free): only free the pixels member if it is non-NULL and the PIXBUF_FLAG_DESTROY_PIXELS is set. 1999-07-02 Raph Levien * art_vpath_bpath.c (art_vpath_render_bez): Bad bad uninitialized variables. * configure.in: added compile warnings. Guess why :) 1999-06-28 Raph Levien * art_svp_point.h: * art_svp_point.c: Added methods for insideness and distance testing, very useful for ::point methods in canvas items. * testart.c: test code to exercise the art_svp_point functions. * Makefile.am: Additional entries for art_svp_point. 1999-06-28 Raph Levien * art_svp_render_aa.c (art_svp_render_aa): Subtle boundary case in realloc code -- was causing nasty segfaults. Wed Jun 23 15:05:43 1999 Raph Levien * art_rgb_svp.c (art_rgb_svp_alpha_opaque_callback): Missed a case in the anti-segfault crusade. Thanks lewing! Wed Jun 23 11:16:42 1999 Raph Levien * art_rgb_svp.c: Made these routines so they won't segfault even if alpha is out of range. Of course, that begs the question of fixing the render routines so they won't _make_ alpha go out of range, but all in good time. Fri Jun 18 17:32:34 1999 Raph Levien * art_vpath_bpath.c (art_bez_path_to_vec): Switched to a new adaptive subdivision algorithm, which (finally!) takes flatness into account. This should result in both smoother curves and faster operation. Sun Jun 13 21:07:20 1999 Raph Levien * art_svp_wind.c (art_svp_rewind_uncrossed): Made the winding rule logic even more correct :). I somehow missed the fact that a clockwise path should be given a winding number of zero; last night's commit tried to make it -1 (which worked for the test cases I was using). Sun Jun 13 01:23:14 1999 Raph Levien * art_svp_wind.c (art_svp_rewind_uncrossed): Change to winding rule logic so that it correctly handles the case where the leftmost segment is negative. * Makefile.am (libart_lgplinc_HEADERS): made art_svp_wind.h a public headerfile. This is needed for the bpath canvas item. I'm not sure this is the correct way to do it, but it will do for now. * art_vpath_bpath.h: * art_vpath_bpath.c (art_bez_path_to_vec): Added const to arg. * art_vpath_bpath.h: Embarrassing typo. * art_bpath.h: Minor tweaks to the #include paths. It is now consistent with the other header files. Wed Jun 9 20:24:45 1999 Raph Levien * art_svp_vpath_stroke.c: Added all remaining line join and cap types, including round, which takes flatness into account. Several new internal functions (art_svp_vpath_stroke_arc) and added flatness argument to a few internal functions. I might want to change the BEVEL join type to MITER for very small turn angles (i.e. within a flatness threshold) for efficiency. * art_misc.h: Added M_SQRT2 constant. Wed Jun 2 21:56:30 1999 Raph Levien * art_svp_vpath_stroke.c (art_svp_vpath_stroke_raw): Made the closed path detection capable of PostScript semantics (i.e. it now senses the difference between ART_MOVETO and ART_MOVETO_OPEN). * art_svp_vpath_stroke.c (art_svp_vpath_stroke_raw): it now filters out successive points that are (nearly) coincident. This fixes some of the crashes and hangs, including Tim Janik's singularity (trying to stroke MOVETO 50, 50; LINETO 50, 50; END). * art_svp_wind.c (art_svp_rewind_uncrossed): added a test to correctly handle empty input svp's. * art_svp_wind.c (art_svp_uncross): added a test to correctly handle empty input svp's. Sun Jan 17 20:53:40 1999 Jeff Garzik * art_affine.c: Include string.h for memcpy. * art_svp_vpath.c: Remove conflicting static func definition. * art_uta_svp.c: Include art_vpath_svp.h for func definition. Mon Jan 4 12:47:47 1999 Raph Levien * art_bpath.c (art_bpath_affine_transform): Stupid misnaming of this function (forgot the "art_"). Thu Dec 31 09:04:23 1998 Raph Levien * art_affine.c (art_affine_rectilinear): Added this function. * art_rect.c (art_drect_affine_transform): Corrected the name (it was right in the .h). Also made it work with non-rectilinear transforms, while I was at it. Thu Dec 17 11:58:24 1998 Raph Levien * art_alphagamma.h: * art_alphagamma.c: The real code for alphagamma. Wed Dec 16 14:18:46 1998 Raph Levien * art_alphagamma.h: * art_alphagamma.c: Added. At present, it only contains a dummy stub. When the real code is added, it supports doing alpha compositing in a gamma-corrected color space (suppressing jaggies). * art_pixbuf.h: * art_pixbuf.c: Added. This is a virtualization layer over a few different kinds of image formats. * art_rgb_pixbuf_affine.h: * art_rgb_pixbuf_affine.c: Added. Supports compositing of generic images over an rgb buffer. * art_affine.h: * art_affine.c (art_affine_expansion): Added this function, which reports the exact scale factor in the case of rotation, scaling, and transformation (an approximate number in the case of shearing or anamorphic distortion). * art_misc.h: * art_misc.c (art_warn): Added. * art_rgb_affine.h: * art_rgb_affine.c: Added alphagamma argument (not yet implemented). * art_rgb_affine_private.c: Fixed typo bug that was causing repaint problems for nonsquare images. * art_rgb_bitmap_affine.h: * art_rgb_bitmap_affine.c: Major speed improvement, probably fixed correctness while I was at it. Added alphagamma argument (not yet implemented). * art_rgb_svp.h: * art_rgb_svp.c: Added alphagamma argument (only implemented in aa case, not yet alpha case). * art_vpath.c: increased perturbation to 2e-3, because the old value (1e-6) was too small. * testart.c: added alphagamma. * Makefile.am: added new files Sun Dec 27 21:45:03 1998 Raph Levien * art_rect.h: * art_rect.c: Added DRect versions of the basic ops (double rather than int). * art_rect_svp.h: * art_rect_svp.c: Added. This computes the bounding box of an svp. Wed Dec 16 14:18:46 1998 Raph Levien * art_alphagamma.h: * art_alphagamma.c: Added. At present, it only contains a dummy stub. When the real code is added, it supports doing alpha compositing in a gamma-corrected color space (suppressing jaggies). * art_pixbuf.h: * art_pixbuf.c: Added. This is a virtualization layer over a few different kinds of image formats. * art_rgb_pixbuf_affine.h: * art_rgb_pixbuf_affine.c: Added. Supports compositing of generic images over an rgb buffer. * art_affine.h: * art_affine.c (art_affine_expansion): Added this function, which reports the exact scale factor in the case of rotation, scaling, and transformation (an approximate number in the case of shearing or anamorphic distortion). * art_misc.h: * art_misc.c (art_warn): Added. * art_rgb_affine.h: * art_rgb_affine.c: Added alphagamma argument (not yet implemented). * art_rgb_affine_private.c: Fixed typo bug that was causing repaint problems for nonsquare images. * art_rgb_bitmap_affine.h: * art_rgb_bitmap_affine.c: Major speed improvement, probably fixed correctness while I was at it. Added alphagamma argument (not yet implemented). * art_rgb_svp.h: * art_rgb_svp.c: Added alphagamma argument (only implemented in aa case, not yet alpha case). * art_vpath.c: increased perturbation to 2e-3, because the old value (1e-6) was too small. * testart.c: added alphagamma. * Makefile.am: added new files Mon Dec 14 00:16:53 1998 Raph Levien * art_affine.c (art_affine_to_string): re-added the "scale" method that was accidentally deleted before check-in. * Makefile.am: added new files Sun Dec 13 00:52:39 1998 Raph Levien * art_affine.h: * art_affine.c: Added. Everything you ever wanted to do with an affine transform. Especially check the functions that generate concise PostScript strings for affine transforms. * art_filterlevel.h: A simple enum for selecting filtering style. * art_rgb_affine.h: * art_rgb_affine.c (art_rgb_affine): Added. This function composites an (opaque) rgb image over an rgb pixel buffer. At present, it's slow and only nearest neighbor filtering is enabled. * art_rgb_rgba_affine.h: * art_rgb_rgba_affine.c: Analogous, but for compositing rgba images. * art_rgb_bitmap_affine.h: * art_rgb_bitmap_affine.c: Analogous, but for compositing bitmap images. * art_rgb_affine_private.c (art_rgb_affine_run): Added. This is a common function used by all the rgb_affine modules to move testing for source image bbox out of the inner loop. * Makefile.am: added the new files * testart.c: exercise the image compositors Wed Dec 9 23:36:35 1998 Raph Levien * art_vpath.c (art_vpath_perturb): Made it deal correctly with closed paths (the MOVETO and closing LINETO have to agree). * art_svp_wind.c: Made the bbox calculations for the resulting svp's correct. * art_svp.h: * art_svp.c: The art_svp_seg_compare function moved here, because it's required in art_svp_ops. * art_svp.c (art_svp_add_segment): It now does bbox calculations. * art_svp_ops.h: * art_svp_ops.c: Added. Populated with basic union, intersection, and diff functions. * art_vpath_svp.h: * art_vpath_svp.c: Added. Populated with a function to convert from sorted to unsorted vector paths * Makefile.am: added the new files * testart.c: exercise the stroke outline and vector path operations. 1998-12-08 Herbert Valerio Riedel * art_svp_wind.c: added #include for memcpy() Sun Dec 6 22:15:12 1998 Raph Levien * art_svp_wind.[ch], art_svp_vpath_stroke.[ch]: Added, but it doesn't work yet. These will do stroke outline and basic vector ops like union, intersect, etc. * art_svp_render_aa.c: Added a simple speedup based on bbox culling. I will want to do more speedups, but none of this is necessary for the freeze. * art_svp_vpath.c: Fixed some bugs in the art_svp_from_vpath in cases where there is more than one subpath. * art_vpath.h: * art_vpath.c (art_vpath_perturb): Added this function. This will help cheat as long as the basic vector ops have numerical stability problems. Fri Dec 4 18:00:38 1998 Raph Levien * art_svp_render_aa.c (art_svp_render_aa): Changed the api slightly, to guarantee that the steps are all within the range from x0 (inclusive) to x1 (exclusive). * art_gray_svp.c, art_gray_svp.h: Added. Populated with functions to render into a simple graymap. * art_rgb.c, art_rgb.c: Added. Populated with fill_run and run_alpha methods. * art_rgb_svp.c, art_rgb_svp.h: Added. Populated with functions to render into an RGB buffer, and to composite over an RGB buffer. * Makefile.am: added art_gray_svp, art_rgb, and art_rgb_svp. * testart.c: test the color and layered rendering. Mon Nov 30 01:30:25 1998 Raph Levien * testart.c: added vector path rendering stuff. Some of it needs to go out of the test framework and into the module, but the api hasn't settled down entirely yet (in the real code, all x's in the step field are within bounds). * art_svp_render_aa.c, art_svp_render_aa.c.h: added. * art_svp_vpath.c, art_svp_vpath.h: added. * art_pathcode.h: added ART_MOVETO_OPEN (libart uses an ART_MOVETO_OPEN code at the beginning to indicate an open path, while PostScript uses the lack of a closepath at the end). * art_vpath_bpath.c, art_vpath_bpath.h: fixed it up, added flatness arg to api. * Makefile.am: added new source files. Wed Nov 25 17:19:44 1998 Raph Levien * art_svp.h, art_svp.c: added, basic constructors for sorted vector paths. Sun Nov 22 23:21:09 1998 Raph Levien * Makefile.am (libart_lgplincdir): Fixed stupid bug in naming of the variable. Sun Nov 22 21:41:13 1998 Raph Levien * art_uta_vpath.c: moved art_uta_union into art_uta_ops. * art_uta_ops.[ch]: added, populated with art_uta_union. Thu Nov 19 00:19:40 1998 Raph Levien * libartConf.sh.in: added * Makefile.am: added creation of libartConf.sh, added -version-info * configure.in: added LIBART_VERSION_INFO, support for libartConf.sh * libart.m4: updated version history :) Wed Nov 18 18:15:20 1998 Raph Levien * configure.in (LIBART_VERSION): set this, so that libart-config --version now works. Wed Nov 18 16:50:58 1998 Raph Levien * libart.m4: added (just copied from esound) * configure.in, Makefile.am: added support for libart-config * libart-config.in: added (mostly copied from esound) Tue Nov 10 12:43:30 1998 Raph Levien * Getting the library in shape for initial checkin to CVS. rl-renderpm-4.0.3/src/libart_lgpl/Makefile.am000066400000000000000000000064711453236046100210770ustar00rootroot00000000000000noinst_PROGRAMS = testart testuta gen_art_config bin_SCRIPTS = \ libart2-config BUILT_SOURCES = art_config.h art_config.h: gen_art_config ./gen_art_config > art_config.h EXTRA_DIST = \ libart-config.in \ libart-2.0.pc.in lib_LTLIBRARIES = libart_lgpl_2.la if OS_WIN32 no_undefined = -no-undefined install-libtool-import-lib: $(INSTALL) .libs/libart_lgpl_2.dll.a $(DESTDIR)$(libdir) uninstall-libtool-import-lib: -rm $(DESTDIR)$(libdir)/libart_lgpl_2.dll.a else install-libtool-import-lib: uninstall-libtool-import-lib: endif if MS_LIB_AVAILABLE noinst_DATA = art_lgpl_2.lib install-ms-lib: $(INSTALL) art_lgpl_2.lib $(DESTDIR)$(libdir) uninstall-ms-lib: -rm $(DESTDIR)$(libdir)/art_lgpl_2.lib art_lgpl_2.lib : libart_lgpl_2.la lib -name:libart_lgpl_2-@LIBART_MAJOR_VERSION@.dll -def:.libs/libart_lgpl_2-@LIBART_MAJOR_VERSION@.dll-def -out:$@ else install-ms-lib: uninstall-ms-lib: endif libart_lgpl_2_la_SOURCES = \ art_affine.c \ art_alphagamma.c \ art_bpath.c \ art_gray_svp.c \ art_misc.c \ art_pixbuf.c \ art_rect.c \ art_rect_svp.c \ art_rect_uta.c \ art_render.c \ art_render_gradient.c \ art_render_mask.c \ art_render_svp.c \ art_rgb.c \ art_rgb_affine.c \ art_rgb_affine_private.c \ art_rgb_affine_private.h \ art_rgb_bitmap_affine.c \ art_rgb_pixbuf_affine.c \ art_rgb_rgba_affine.c \ art_rgb_a_affine.c \ art_rgba.c \ art_rgb_svp.c \ art_svp.c \ art_svp_intersect.c \ art_svp_ops.c \ art_svp_point.c \ art_svp_render_aa.c \ art_svp_vpath.c \ art_svp_vpath_stroke.c \ art_svp_wind.c \ art_uta.c \ art_uta_ops.c \ art_uta_rect.c \ art_uta_vpath.c \ art_uta_svp.c \ art_vpath.c \ art_vpath_bpath.c \ art_vpath_dash.c \ art_vpath_svp.c \ libart-features.c libart_lgpl_2_la_LDFLAGS = -version-info @LIBART_VERSION_INFO@ $(no_undefined) libart_lgpl_2_la_LIBADD = -lm libart_lgplincdir = $(includedir)/libart-2.0/libart_lgpl libart_lgplinc_HEADERS = \ art_affine.h \ art_alphagamma.h \ art_bpath.h \ art_config.h \ art_filterlevel.h \ art_gray_svp.h \ art_misc.h \ art_pathcode.h \ art_pixbuf.h \ art_point.h \ art_rect.h \ art_rect_svp.h \ art_rect_uta.h \ art_render.h \ art_render_gradient.h \ art_render_mask.h \ art_render_svp.h \ art_rgb.h \ art_rgb_affine.h \ art_rgb_bitmap_affine.h \ art_rgb_pixbuf_affine.h \ art_rgb_rgba_affine.h \ art_rgb_a_affine.h \ art_rgb_svp.h \ art_rgba.h \ art_svp.h \ art_svp_intersect.h \ art_svp_ops.h \ art_svp_point.h \ art_svp_render_aa.h \ art_svp_vpath.h \ art_svp_vpath_stroke.h \ art_svp_wind.h \ art_uta.h \ art_uta_ops.h \ art_uta_rect.h \ art_uta_vpath.h \ art_uta_svp.h \ art_vpath.h \ art_vpath_bpath.h \ art_vpath_dash.h \ art_vpath_svp.h \ libart.h \ libart-features.h INCLUDES = -I$(top_srcdir) -I$(top_builddir) -DLIBART_COMPILATION DEPS = $(top_builddir)/libart_lgpl_2.la LDADDS = $(top_builddir)/libart_lgpl_2.la testart_SOURCES=testart.c testart_LDFLAGS = testart_DEPENDENCIES = $(DEPS) testart_LDADD = $(LDADDS) -lm testuta_SOURCES=testuta.c testuta_LDFLAGS = testuta_DEPENDENCIES = $(DEPS) testuta_LDADD = $(LDADDS) -lm tests: testart testuta pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libart-2.0.pc libart2-config: libart-config cp -f libart-config libart2-config install-data-local: install-ms-lib install-libtool-import-lib uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib rl-renderpm-4.0.3/src/libart_lgpl/NEWS000066400000000000000000000000761453236046100175350ustar00rootroot00000000000000Please see http://www.levien.com/libart/ for the latest news. rl-renderpm-4.0.3/src/libart_lgpl/README000066400000000000000000000011131453236046100177070ustar00rootroot00000000000000This is the LGPL'd component of libart. All functions needed for running the Gnome canvas, and for printing support, will be going in here. The GPL'd component will be getting various enhanced functions for specific applications. Libart is free software. It is also for sale. For information about licensing libart, please contact Raph Levien . Contributions to the codebase are also very welcome, but the copyright must be assigned in writing to preserve the licensing flexibility. For more information about libart, see the web page: http://www.levien.com/libart/ rl-renderpm-4.0.3/src/libart_lgpl/README.CVS000066400000000000000000000007071453236046100203510ustar00rootroot00000000000000Welcome to the libart source tree! This code is being developed in a "free software for sale" model. Thus, it's available under a free software license (LGPL for this module), but I'm also requesting that for all changes to the code the copyright gets assigned back to me. So if you want to contribute, please do, but contact me about getting the copyright assigned. Otherwise, I will have to back your changes out. Thanks! Raph Levien rl-renderpm-4.0.3/src/libart_lgpl/acconfig.h000066400000000000000000000000361453236046100207540ustar00rootroot00000000000000#undef PACKAGE #undef VERSION rl-renderpm-4.0.3/src/libart_lgpl/art_affine.c000066400000000000000000000275041453236046100213050ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Simple manipulations with affine transformations */ #include "config.h" #include "art_affine.h" #include "art_misc.h" /* for M_PI */ #include #include /* for sprintf */ #include /* for strcpy */ /* According to a strict interpretation of the libart structure, this routine should go into its own module, art_point_affine. However, it's only two lines of code, and it can be argued that it is one of the natural basic functions of an affine transformation. */ /** * art_affine_point: Do an affine transformation of a point. * @dst: Where the result point is stored. * @src: The original point. @ @affine: The affine transformation. **/ void art_affine_point (ArtPoint *dst, const ArtPoint *src, const double affine[6]) { double x, y; x = src->x; y = src->y; dst->x = x * affine[0] + y * affine[2] + affine[4]; dst->y = x * affine[1] + y * affine[3] + affine[5]; } /** * art_affine_invert: Find the inverse of an affine transformation. * @dst: Where the resulting affine is stored. * @src: The original affine transformation. * * All non-degenerate affine transforms are invertible. If the original * affine is degenerate or nearly so, expect numerical instability and * very likely core dumps on Alpha and other fp-picky architectures. * Otherwise, @dst multiplied with @src, or @src multiplied with @dst * will be (to within roundoff error) the identity affine. **/ void art_affine_invert (double dst[6], const double src[6]) { double r_det; r_det = 1.0 / (src[0] * src[3] - src[1] * src[2]); dst[0] = src[3] * r_det; dst[1] = -src[1] * r_det; dst[2] = -src[2] * r_det; dst[3] = src[0] * r_det; dst[4] = -src[4] * dst[0] - src[5] * dst[2]; dst[5] = -src[4] * dst[1] - src[5] * dst[3]; } /** * art_affine_flip: Flip an affine transformation horizontally and/or vertically. * @dst_affine: Where the resulting affine is stored. * @src_affine: The original affine transformation. * @horiz: Whether or not to flip horizontally. * @vert: Whether or not to flip horizontally. * * Flips the affine transform. FALSE for both @horiz and @vert implements * a simple copy operation. TRUE for both @horiz and @vert is a * 180 degree rotation. It is ok for @src_affine and @dst_affine to * be equal pointers. **/ void art_affine_flip (double dst_affine[6], const double src_affine[6], int horz, int vert) { dst_affine[0] = horz ? - src_affine[0] : src_affine[0]; dst_affine[1] = horz ? - src_affine[1] : src_affine[1]; dst_affine[2] = vert ? - src_affine[2] : src_affine[2]; dst_affine[3] = vert ? - src_affine[3] : src_affine[3]; dst_affine[4] = horz ? - src_affine[4] : src_affine[4]; dst_affine[5] = vert ? - src_affine[5] : src_affine[5]; } #define EPSILON 1e-6 /* It's ridiculous I have to write this myself. This is hardcoded to six digits of precision, which is good enough for PostScript. The return value is the number of characters (i.e. strlen (str)). It is no more than 12. */ static int art_ftoa (char str[80], double x) { char *p = str; int i, j; p = str; if (fabs (x) < EPSILON / 2) { strcpy (str, "0"); return 1; } if (x < 0) { *p++ = '-'; x = -x; } if ((int)floor(x + EPSILON / 2) < 1) { *p++ = '0'; *p++ = '.'; i = sprintf (p, "%06d", (int)floor ((x + EPSILON / 2) * 1e6)); while (i && p[i - 1] == '0') i--; if (i == 0) i--; p += i; } else if (x < 1e6) { i = sprintf (p, "%d", (int)floor (x + EPSILON / 2)); p += i; if (i < 6) { int ix; *p++ = '.'; x -= floor (x + EPSILON / 2); for (j = i; j < 6; j++) x *= 10; ix = (int) floor (x + 0.5); for (j = 0; j < i; j++) ix *= 10; /* A cheap hack, this routine can round wrong for fractions near one. */ if (ix == 1000000) ix = 999999; sprintf (p, "%06d", ix); i = 6 - i; while (i && p[i - 1] == '0') i--; if (i == 0) i--; p += i; } } else p += sprintf (p, "%g", x); *p = '\0'; return p - str; } #include /** * art_affine_to_string: Convert affine transformation to concise PostScript string representation. * @str: Where to store the resulting string. * @src: The affine transform. * * Converts an affine transform into a bit of PostScript code that * implements the transform. Special cases of scaling, rotation, and * translation are detected, and the corresponding PostScript * operators used (this greatly aids understanding the output * generated). The identity transform is mapped to the null string. **/ void art_affine_to_string (char str[128], const double src[6]) { char tmp[80]; int i, ix; #if 0 for (i = 0; i < 1000; i++) { double d = rand () * .1 / RAND_MAX; art_ftoa (tmp, d); printf ("%g %f %s\n", d, d, tmp); } #endif if (fabs (src[4]) < EPSILON && fabs (src[5]) < EPSILON) { /* could be scale or rotate */ if (fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON) { /* scale */ if (fabs (src[0] - 1) < EPSILON && fabs (src[3] - 1) < EPSILON) { /* identity transform */ str[0] = '\0'; return; } else { ix = 0; ix += art_ftoa (str + ix, src[0]); str[ix++] = ' '; ix += art_ftoa (str + ix, src[3]); strcpy (str + ix, " scale"); return; } } else { /* could be rotate */ if (fabs (src[0] - src[3]) < EPSILON && fabs (src[1] + src[2]) < EPSILON && fabs (src[0] * src[0] + src[1] * src[1] - 1) < 2 * EPSILON) { double theta; theta = (180 / M_PI) * atan2 (src[1], src[0]); art_ftoa (tmp, theta); sprintf (str, "%s rotate", tmp); return; } } } else { /* could be translate */ if (fabs (src[0] - 1) < EPSILON && fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON && fabs (src[3] - 1) < EPSILON) { ix = 0; ix += art_ftoa (str + ix, src[4]); str[ix++] = ' '; ix += art_ftoa (str + ix, src[5]); strcpy (str + ix, " translate"); return; } } ix = 0; str[ix++] = '['; str[ix++] = ' '; for (i = 0; i < 6; i++) { ix += art_ftoa (str + ix, src[i]); str[ix++] = ' '; } strcpy (str + ix, "] concat"); } /** * art_affine_multiply: Multiply two affine transformation matrices. * @dst: Where to store the result. * @src1: The first affine transform to multiply. * @src2: The second affine transform to multiply. * * Multiplies two affine transforms together, i.e. the resulting @dst * is equivalent to doing first @src1 then @src2. Note that the * PostScript concat operator multiplies on the left, i.e. "M concat" * is equivalent to "CTM = multiply (M, CTM)"; * * It is safe to call this function with @dst equal to @src1 or @src2. **/ void art_affine_multiply (double dst[6], const double src1[6], const double src2[6]) { double d0, d1, d2, d3, d4, d5; d0 = src1[0] * src2[0] + src1[1] * src2[2]; d1 = src1[0] * src2[1] + src1[1] * src2[3]; d2 = src1[2] * src2[0] + src1[3] * src2[2]; d3 = src1[2] * src2[1] + src1[3] * src2[3]; d4 = src1[4] * src2[0] + src1[5] * src2[2] + src2[4]; d5 = src1[4] * src2[1] + src1[5] * src2[3] + src2[5]; dst[0] = d0; dst[1] = d1; dst[2] = d2; dst[3] = d3; dst[4] = d4; dst[5] = d5; } /** * art_affine_identity: Set up the identity matrix. * @dst: Where to store the resulting affine transform. * * Sets up an identity matrix. **/ void art_affine_identity (double dst[6]) { dst[0] = 1; dst[1] = 0; dst[2] = 0; dst[3] = 1; dst[4] = 0; dst[5] = 0; } /** * art_affine_scale: Set up a scaling matrix. * @dst: Where to store the resulting affine transform. * @sx: X scale factor. * @sy: Y scale factor. * * Sets up a scaling matrix. **/ void art_affine_scale (double dst[6], double sx, double sy) { dst[0] = sx; dst[1] = 0; dst[2] = 0; dst[3] = sy; dst[4] = 0; dst[5] = 0; } /** * art_affine_rotate: Set up a rotation affine transform. * @dst: Where to store the resulting affine transform. * @theta: Rotation angle in degrees. * * Sets up a rotation matrix. In the standard libart coordinate * system, in which increasing y moves downward, this is a * counterclockwise rotation. In the standard PostScript coordinate * system, which is reversed in the y direction, it is a clockwise * rotation. **/ void art_affine_rotate (double dst[6], double theta) { double s, c; s = sin (theta * M_PI / 180.0); c = cos (theta * M_PI / 180.0); dst[0] = c; dst[1] = s; dst[2] = -s; dst[3] = c; dst[4] = 0; dst[5] = 0; } /** * art_affine_shear: Set up a shearing matrix. * @dst: Where to store the resulting affine transform. * @theta: Shear angle in degrees. * * Sets up a shearing matrix. In the standard libart coordinate system * and a small value for theta, || becomes \\. Horizontal lines remain * unchanged. **/ void art_affine_shear (double dst[6], double theta) { double t; t = tan (theta * M_PI / 180.0); dst[0] = 1; dst[1] = 0; dst[2] = t; dst[3] = 1; dst[4] = 0; dst[5] = 0; } /** * art_affine_translate: Set up a translation matrix. * @dst: Where to store the resulting affine transform. * @tx: X translation amount. * @tx: Y translation amount. * * Sets up a translation matrix. **/ void art_affine_translate (double dst[6], double tx, double ty) { dst[0] = 1; dst[1] = 0; dst[2] = 0; dst[3] = 1; dst[4] = tx; dst[5] = ty; } /** * art_affine_expansion: Find the affine's expansion factor. * @src: The affine transformation. * * Finds the expansion factor, i.e. the square root of the factor * by which the affine transform affects area. In an affine transform * composed of scaling, rotation, shearing, and translation, returns * the amount of scaling. * * Return value: the expansion factor. **/ double art_affine_expansion (const double src[6]) { return sqrt (fabs (src[0] * src[3] - src[1] * src[2])); } /** * art_affine_rectilinear: Determine whether the affine transformation is rectilinear. * @src: The original affine transformation. * * Determines whether @src is rectilinear, i.e. grid-aligned * rectangles are transformed to other grid-aligned rectangles. The * implementation has epsilon-tolerance for roundoff errors. * * Return value: TRUE if @src is rectilinear. **/ int art_affine_rectilinear (const double src[6]) { return ((fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON) || (fabs (src[0]) < EPSILON && fabs (src[3]) < EPSILON)); } /** * art_affine_equal: Determine whether two affine transformations are equal. * @matrix1: An affine transformation. * @matrix2: Another affine transformation. * * Determines whether @matrix1 and @matrix2 are equal, with * epsilon-tolerance for roundoff errors. * * Return value: TRUE if @matrix1 and @matrix2 are equal. **/ int art_affine_equal (double matrix1[6], double matrix2[6]) { return (fabs (matrix1[0] - matrix2[0]) < EPSILON && fabs (matrix1[1] - matrix2[1]) < EPSILON && fabs (matrix1[2] - matrix2[2]) < EPSILON && fabs (matrix1[3] - matrix2[3]) < EPSILON && fabs (matrix1[4] - matrix2[4]) < EPSILON && fabs (matrix1[5] - matrix2[5]) < EPSILON); } rl-renderpm-4.0.3/src/libart_lgpl/art_affine.h000066400000000000000000000051401453236046100213020ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_AFFINE_H__ #define __ART_AFFINE_H__ #ifdef LIBART_COMPILATION #include "art_point.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void art_affine_point (ArtPoint *dst, const ArtPoint *src, const double affine[6]); void art_affine_invert (double dst_affine[6], const double src_affine[6]); /* flip the matrix, FALSE, FALSE is a simple copy operation, and TRUE, TRUE equals a rotation by 180 degrees */ void art_affine_flip (double dst_affine[6], const double src_affine[6], int horz, int vert); void art_affine_to_string (char str[128], const double src[6]); void art_affine_multiply (double dst[6], const double src1[6], const double src2[6]); /* set up the identity matrix */ void art_affine_identity (double dst[6]); /* set up a scaling matrix */ void art_affine_scale (double dst[6], double sx, double sy); /* set up a rotation matrix; theta is given in degrees */ void art_affine_rotate (double dst[6], double theta); /* set up a shearing matrix; theta is given in degrees */ void art_affine_shear (double dst[6], double theta); /* set up a translation matrix */ void art_affine_translate (double dst[6], double tx, double ty); /* find the affine's "expansion factor", i.e. the scale amount */ double art_affine_expansion (const double src[6]); /* Determine whether the affine transformation is rectilinear, i.e. whether a rectangle aligned to the grid is transformed into another rectangle aligned to the grid. */ int art_affine_rectilinear (const double src[6]); /* Determine whether two affine transformations are equal within grid allignment */ int art_affine_equal (double matrix1[6], double matrix2[6]); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_AFFINE_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_alphagamma.c000066400000000000000000000047631453236046100221470ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Some functions to build alphagamma tables */ #include "config.h" #include "art_alphagamma.h" #include /** * art_alphagamma_new: Create a new #ArtAlphaGamma. * @gamma: Gamma value. * * Create a new #ArtAlphaGamma for a specific value of @gamma. When * correctly implemented (which is generally not the case in libart), * alpha compositing with an alphagamma parameter is equivalent to * applying the gamma transformation to source images, doing the alpha * compositing (in linear intensity space), then applying the inverse * gamma transformation, bringing it back to a gamma-adjusted * intensity space. * * Return value: The newly created #ArtAlphaGamma. **/ ArtAlphaGamma * art_alphagamma_new (double gamma) { int tablesize; ArtAlphaGamma *alphagamma; int i; int *table; art_u8 *invtable; double s, r_gamma; tablesize = (int) ceil (gamma * 8); if (tablesize < 10) tablesize = 10; alphagamma = (ArtAlphaGamma *)art_alloc (sizeof(ArtAlphaGamma) + ((1 << tablesize) - 1) * sizeof(art_u8)); alphagamma->gamma = gamma; alphagamma->invtable_size = tablesize; table = alphagamma->table; for (i = 0; i < 256; i++) table[i] = (int)floor (((1 << tablesize) - 1) * pow (i * (1.0 / 255), gamma) + 0.5); invtable = alphagamma->invtable; s = 1.0 / ((1 << tablesize) - 1); r_gamma = 1.0 / gamma; for (i = 0; i < 1 << tablesize; i++) invtable[i] = (int)floor (255 * pow (i * s, r_gamma) + 0.5); return alphagamma; } /** * art_alphagamma_free: Free an #ArtAlphaGamma. * @alphagamma: An #ArtAlphaGamma. * * Frees the #ArtAlphaGamma. **/ void art_alphagamma_free (ArtAlphaGamma *alphagamma) { art_free (alphagamma); } rl-renderpm-4.0.3/src/libart_lgpl/art_alphagamma.h000066400000000000000000000026201453236046100221420ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_ALPHAGAMMA_H__ #define __ART_ALPHAGAMMA_H__ /* Alphagamma tables */ #ifdef LIBART_COMPILATION #include "art_misc.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtAlphaGamma ArtAlphaGamma; struct _ArtAlphaGamma { /*< private >*/ double gamma; int invtable_size; int table[256]; art_u8 invtable[1]; }; ArtAlphaGamma * art_alphagamma_new (double gamma); void art_alphagamma_free (ArtAlphaGamma *alphagamma); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_bpath.c000066400000000000000000000046461453236046100211550ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Basic constructors and operations for bezier paths */ #include "config.h" #include "art_bpath.h" #include /** * art_bpath_affine_transform: Affine transform an #ArtBpath. * @src: The source #ArtBpath. * @matrix: The affine transform. * * Affine transform the bezpath, returning a newly allocated #ArtBpath * (allocated using art_alloc()). * * Result (x', y') = (matrix[0] * x + matrix[2] * y + matrix[4], * matrix[1] * x + matrix[3] * y + matrix[5]) * * Return value: the transformed #ArtBpath. **/ ArtBpath * art_bpath_affine_transform (const ArtBpath *src, const double matrix[6]) { int i; int size; ArtBpath *new; ArtPathcode code; double x, y; for (i = 0; src[i].code != ART_END; i++); size = i; new = art_new (ArtBpath, size + 1); for (i = 0; i < size; i++) { code = src[i].code; new[i].code = code; if (code == ART_CURVETO) { x = src[i].x1; y = src[i].y1; new[i].x1 = matrix[0] * x + matrix[2] * y + matrix[4]; new[i].y1 = matrix[1] * x + matrix[3] * y + matrix[5]; x = src[i].x2; y = src[i].y2; new[i].x2 = matrix[0] * x + matrix[2] * y + matrix[4]; new[i].y2 = matrix[1] * x + matrix[3] * y + matrix[5]; } else { new[i].x1 = 0; new[i].y1 = 0; new[i].x2 = 0; new[i].y2 = 0; } x = src[i].x3; y = src[i].y3; new[i].x3 = matrix[0] * x + matrix[2] * y + matrix[4]; new[i].y3 = matrix[1] * x + matrix[3] * y + matrix[5]; } new[i].code = ART_END; new[i].x1 = 0; new[i].y1 = 0; new[i].x2 = 0; new[i].y2 = 0; new[i].x3 = 0; new[i].y3 = 0; return new; } rl-renderpm-4.0.3/src/libart_lgpl/art_bpath.h000066400000000000000000000030301453236046100211440ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_BPATH_H__ #define __ART_BPATH_H__ #ifdef LIBART_COMPILATION #include "art_misc.h" #include "art_point.h" #include "art_pathcode.h" #else #include #include #include #endif /* Basic data structures and constructors for bezier paths */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtBpath ArtBpath; struct _ArtBpath { /*< public >*/ ArtPathcode code; double x1; double y1; double x2; double y2; double x3; double y3; }; ArtBpath * art_bpath_affine_transform (const ArtBpath *src, const double matrix[6]); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_BPATH_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_config.h000066400000000000000000000004461453236046100213230ustar00rootroot00000000000000/* Automatically generated by setup.py */ #ifndef _ART_CONFIG_H # define _ART_CONFIG_H # define ART_SIZEOF_CHAR 1 # define ART_SIZEOF_SHORT 2 # define ART_SIZEOF_INT 4 # define ART_SIZEOF_LONG 8 typedef unsigned char art_u8; typedef unsigned short art_u16; typedef unsigned int art_u32; #endif rl-renderpm-4.0.3/src/libart_lgpl/art_filterlevel.h000066400000000000000000000045321453236046100223730ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_FILTERLEVEL_H__ #define __ART_FILTERLEVEL_H__ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef enum { ART_FILTER_NEAREST, ART_FILTER_TILES, ART_FILTER_BILINEAR, ART_FILTER_HYPER } ArtFilterLevel; /* NEAREST is nearest neighbor. It is the fastest and lowest quality. TILES is an accurate simulation of the PostScript image operator without any interpolation enabled; each pixel is rendered as a tiny parallelogram of solid color, the edges of which are implemented with antialiasing. It resembles nearest neighbor for enlargement, and bilinear for reduction. BILINEAR is bilinear interpolation. For enlargement, it is equivalent to point-sampling the ideal bilinear-interpolated image. For reduction, it is equivalent to laying down small tiles and integrating over the coverage area. HYPER is the highest quality reconstruction function. It is derived from the hyperbolic filters in Wolberg's "Digital Image Warping," and is formally defined as the hyperbolic-filter sampling the ideal hyperbolic-filter interpolated image (the filter is designed to be idempotent for 1:1 pixel mapping). It is the slowest and highest quality. Note: at this stage of implementation, most filter modes are likely not to be implemented. Note: cubic filtering is missing from this list, because there isn't much point - hyper is just as fast to implement and slightly better in quality. */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_PATHCODE_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_gray_svp.c000066400000000000000000000064351453236046100217070ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Render a sorted vector path into a graymap. */ #include "config.h" #include "art_gray_svp.h" #include /* for memset */ #include "art_misc.h" #include "art_svp.h" #include "art_svp_render_aa.h" typedef struct _ArtGraySVPData ArtGraySVPData; struct _ArtGraySVPData { art_u8 *buf; int rowstride; int x0, x1; }; static void art_gray_svp_callback (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtGraySVPData *data = (ArtGraySVPData *)callback_data; art_u8 *linebuf; int run_x0, run_x1; int running_sum = start; int x0, x1; int k; #if 0 printf ("start = %d", start); running_sum = start; for (k = 0; k < n_steps; k++) { running_sum += steps[k].delta; printf (" %d:%d", steps[k].x, running_sum >> 16); } printf ("\n"); #endif linebuf = data->buf; x0 = data->x0; x1 = data->x1; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0) memset (linebuf, running_sum >> 16, run_x1 - x0); for (k = 0; k < n_steps - 1; k++) { running_sum += steps[k].delta; run_x0 = run_x1; run_x1 = steps[k + 1].x; if (run_x1 > run_x0) memset (linebuf + run_x0 - x0, running_sum >> 16, run_x1 - run_x0); } running_sum += steps[k].delta; if (x1 > run_x1) memset (linebuf + run_x1 - x0, running_sum >> 16, x1 - run_x1); } else { memset (linebuf, running_sum >> 16, x1 - x0); } data->buf += data->rowstride; } /** * art_gray_svp_aa: Render the vector path into the bytemap. * @svp: The SVP to render. * @x0: The view window's left coord. * @y0: The view window's top coord. * @x1: The view window's right coord. * @y1: The view window's bottom coord. * @buf: The buffer where the bytemap is stored. * @rowstride: the rowstride for @buf. * * Each pixel gets a value proportional to the area within the pixel * overlapping the (filled) SVP. Pixel (x, y) is stored at: * * @buf[(y - * @y0) * @rowstride + (x - @x0)] * * All pixels @x0 <= x < @x1, @y0 <= y < @y1 are generated. A * stored value of zero is no coverage, and a value of 255 is full * coverage. The area within the pixel (x, y) is the region covered * by [x..x+1] and [y..y+1]. **/ void art_gray_svp_aa (const ArtSVP *svp, int x0, int y0, int x1, int y1, art_u8 *buf, int rowstride) { ArtGraySVPData data; data.buf = buf; data.rowstride = rowstride; data.x0 = x0; data.x1 = x1; art_svp_render_aa (svp, x0, y0, x1, y1, art_gray_svp_callback, &data); } rl-renderpm-4.0.3/src/libart_lgpl/art_gray_svp.h000066400000000000000000000024721453236046100217110ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Render a sorted vector path into a graymap. */ #ifndef __ART_GRAY_SVP_H__ #define __ART_GRAY_SVP_H__ #ifdef LIBART_COMPILATION #include "art_misc.h" #include "art_svp.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void art_gray_svp_aa (const ArtSVP *svp, int x0, int y0, int x1, int y1, art_u8 *buf, int rowstride); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_GRAY_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_misc.c000066400000000000000000000036471453236046100210120ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Various utility functions RLL finds useful. */ #include "config.h" #include "art_misc.h" #ifdef HAVE_UINSTD_H # include #endif #include #include /** * art_die: Print the error message to stderr and exit with a return code of 1. * @fmt: The printf-style format for the error message. * * Used for dealing with severe errors. **/ void art_die (const char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); exit (1); } /** * art_warn: Print the warning message to stderr. * @fmt: The printf-style format for the warning message. * * Used for generating warnings. **/ void art_warn (const char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); } /** * art_dprint: Print the debug message to stderr. * @fmt: The printf-style format for the debug message. * * Used for generating debug output. **/ void art_dprint (const char *fmt, ...) { va_list ap; va_start (ap, fmt); #ifndef NOSTDERR vfprintf (stderr, fmt, ap); #else vfprintf (stdout, fmt, ap); #endif va_end (ap); } rl-renderpm-4.0.3/src/libart_lgpl/art_misc.h000066400000000000000000000056151453236046100210140ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Simple macros to set up storage allocation and basic types for libart functions. */ #ifndef __ART_MISC_H__ #define __ART_MISC_H__ #include /* for malloc, etc. */ /* The art_config.h file is automatically generated by gen_art_config.c and contains definitions of ART_SIZEOF_{CHAR,SHORT,INT,LONG} and art_u{8,16,32}. */ #ifdef LIBART_COMPILATION #include "art_config.h" #else #include #endif #define art_alloc malloc #define art_free free #define art_realloc realloc /* These aren't, strictly speaking, configuration macros, but they're damn handy to have around, and may be worth playing with for debugging. */ #define art_new(type, n) ((type *)art_alloc ((n) * sizeof(type))) #define art_renew(p, type, n) ((type *)art_realloc (p, (n) * sizeof(type))) /* This one must be used carefully - in particular, p and max should be variables. They can also be pstruct->el lvalues. */ #define art_expand(p, type, max) do { if(max) { p = art_renew (p, type, max <<= 1); } else { max = 1; p = art_new(type, 1); } } while (0) typedef int art_boolean; #define ART_FALSE 0 #define ART_TRUE 1 /* define pi */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* M_PI */ #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ #endif /* M_SQRT2 */ /* Provide macros to feature the GCC function attribute. */ #if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)) #define ART_GNUC_PRINTF( format_idx, arg_idx ) \ __attribute__((format (printf, format_idx, arg_idx))) #define ART_GNUC_NORETURN \ __attribute__((noreturn)) #else /* !__GNUC__ */ #define ART_GNUC_PRINTF( format_idx, arg_idx ) #define ART_GNUC_NORETURN #endif /* !__GNUC__ */ #ifdef __cplusplus extern "C" { #endif void ART_GNUC_NORETURN art_die (const char *fmt, ...) ART_GNUC_PRINTF (1, 2); void art_warn (const char *fmt, ...) ART_GNUC_PRINTF (1, 2); void art_dprint (const char *fmt, ...) ART_GNUC_PRINTF (1, 2); #ifdef __cplusplus } #endif #define ART_USE_NEW_INTERSECTOR #endif /* __ART_MISC_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_pathcode.h000066400000000000000000000021531453236046100216420ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_PATHCODE_H__ #define __ART_PATHCODE_H__ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef enum { ART_MOVETO, ART_MOVETO_OPEN, ART_CURVETO, ART_LINETO, ART_END } ArtPathcode; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_PATHCODE_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_pixbuf.c000066400000000000000000000216171453236046100213510ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_pixbuf.h" #include "art_misc.h" #include /** * art_pixbuf_new_rgb_dnotify: Create a new RGB #ArtPixBuf with explicit destroy notification. * @pixels: A buffer containing the actual pixel data. * @width: The width of the pixbuf. * @height: The height of the pixbuf. * @rowstride: The rowstride of the pixbuf. * @dfunc_data: The private data passed to @dfunc. * @dfunc: The destroy notification function. * * Creates a generic data structure for holding a buffer of RGB * pixels. It is possible to think of an #ArtPixBuf as a * virtualization over specific pixel buffer formats. * * @dfunc is called with @dfunc_data and @pixels as arguments when the * #ArtPixBuf is destroyed. Using a destroy notification function * allows a wide range of memory management disciplines for the pixel * memory. A NULL value for @dfunc is also allowed and means that no * special action will be taken on destruction. * * Return value: The newly created #ArtPixBuf. **/ ArtPixBuf * art_pixbuf_new_rgb_dnotify (art_u8 *pixels, int width, int height, int rowstride, void *dfunc_data, ArtDestroyNotify dfunc) { ArtPixBuf *pixbuf; pixbuf = art_new (ArtPixBuf, 1); pixbuf->format = ART_PIX_RGB; pixbuf->n_channels = 3; pixbuf->has_alpha = 0; pixbuf->bits_per_sample = 8; pixbuf->pixels = (art_u8 *) pixels; pixbuf->width = width; pixbuf->height = height; pixbuf->rowstride = rowstride; pixbuf->destroy_data = dfunc_data; pixbuf->destroy = dfunc; return pixbuf; } /** * art_pixbuf_new_rgba_dnotify: Create a new RGBA #ArtPixBuf with explicit destroy notification. * @pixels: A buffer containing the actual pixel data. * @width: The width of the pixbuf. * @height: The height of the pixbuf. * @rowstride: The rowstride of the pixbuf. * @dfunc_data: The private data passed to @dfunc. * @dfunc: The destroy notification function. * * Creates a generic data structure for holding a buffer of RGBA * pixels. It is possible to think of an #ArtPixBuf as a * virtualization over specific pixel buffer formats. * * @dfunc is called with @dfunc_data and @pixels as arguments when the * #ArtPixBuf is destroyed. Using a destroy notification function * allows a wide range of memory management disciplines for the pixel * memory. A NULL value for @dfunc is also allowed and means that no * special action will be taken on destruction. * * Return value: The newly created #ArtPixBuf. **/ ArtPixBuf * art_pixbuf_new_rgba_dnotify (art_u8 *pixels, int width, int height, int rowstride, void *dfunc_data, ArtDestroyNotify dfunc) { ArtPixBuf *pixbuf; pixbuf = art_new (ArtPixBuf, 1); pixbuf->format = ART_PIX_RGB; pixbuf->n_channels = 4; pixbuf->has_alpha = 1; pixbuf->bits_per_sample = 8; pixbuf->pixels = (art_u8 *) pixels; pixbuf->width = width; pixbuf->height = height; pixbuf->rowstride = rowstride; pixbuf->destroy_data = dfunc_data; pixbuf->destroy = dfunc; return pixbuf; } /** * art_pixbuf_new_const_rgb: Create a new RGB #ArtPixBuf with constant pixel data. * @pixels: A buffer containing the actual pixel data. * @width: The width of the pixbuf. * @height: The height of the pixbuf. * @rowstride: The rowstride of the pixbuf. * * Creates a generic data structure for holding a buffer of RGB * pixels. It is possible to think of an #ArtPixBuf as a * virtualization over specific pixel buffer formats. * * No action is taken when the #ArtPixBuf is destroyed. Thus, this * function is useful when the pixel data is constant or statically * allocated. * * Return value: The newly created #ArtPixBuf. **/ ArtPixBuf * art_pixbuf_new_const_rgb (const art_u8 *pixels, int width, int height, int rowstride) { return art_pixbuf_new_rgb_dnotify ((art_u8 *) pixels, width, height, rowstride, NULL, NULL); } /** * art_pixbuf_new_const_rgba: Create a new RGBA #ArtPixBuf with constant pixel data. * @pixels: A buffer containing the actual pixel data. * @width: The width of the pixbuf. * @height: The height of the pixbuf. * @rowstride: The rowstride of the pixbuf. * * Creates a generic data structure for holding a buffer of RGBA * pixels. It is possible to think of an #ArtPixBuf as a * virtualization over specific pixel buffer formats. * * No action is taken when the #ArtPixBuf is destroyed. Thus, this * function is suitable when the pixel data is constant or statically * allocated. * * Return value: The newly created #ArtPixBuf. **/ ArtPixBuf * art_pixbuf_new_const_rgba (const art_u8 *pixels, int width, int height, int rowstride) { return art_pixbuf_new_rgba_dnotify ((art_u8 *) pixels, width, height, rowstride, NULL, NULL); } static void art_pixel_destroy (void *func_data, void *data) { art_free (data); } /** * art_pixbuf_new_rgb: Create a new RGB #ArtPixBuf. * @pixels: A buffer containing the actual pixel data. * @width: The width of the pixbuf. * @height: The height of the pixbuf. * @rowstride: The rowstride of the pixbuf. * * Creates a generic data structure for holding a buffer of RGB * pixels. It is possible to think of an #ArtPixBuf as a * virtualization over specific pixel buffer formats. * * The @pixels buffer is freed with art_free() when the #ArtPixBuf is * destroyed. Thus, this function is suitable when the pixel data is * allocated with art_alloc(). * * Return value: The newly created #ArtPixBuf. **/ ArtPixBuf * art_pixbuf_new_rgb (art_u8 *pixels, int width, int height, int rowstride) { return art_pixbuf_new_rgb_dnotify (pixels, width, height, rowstride, NULL, art_pixel_destroy); } /** * art_pixbuf_new_rgba: Create a new RGBA #ArtPixBuf. * @pixels: A buffer containing the actual pixel data. * @width: The width of the pixbuf. * @height: The height of the pixbuf. * @rowstride: The rowstride of the pixbuf. * * Creates a generic data structure for holding a buffer of RGBA * pixels. It is possible to think of an #ArtPixBuf as a * virtualization over specific pixel buffer formats. * * The @pixels buffer is freed with art_free() when the #ArtPixBuf is * destroyed. Thus, this function is suitable when the pixel data is * allocated with art_alloc(). * * Return value: The newly created #ArtPixBuf. **/ ArtPixBuf * art_pixbuf_new_rgba (art_u8 *pixels, int width, int height, int rowstride) { return art_pixbuf_new_rgba_dnotify (pixels, width, height, rowstride, NULL, art_pixel_destroy); } /** * art_pixbuf_free: Destroy an #ArtPixBuf. * @pixbuf: The #ArtPixBuf to be destroyed. * * Destroys the #ArtPixBuf, calling the destroy notification function * (if non-NULL) so that the memory for the pixel buffer can be * properly reclaimed. **/ void art_pixbuf_free (ArtPixBuf *pixbuf) { ArtDestroyNotify destroy = pixbuf->destroy; void *destroy_data = pixbuf->destroy_data; art_u8 *pixels = pixbuf->pixels; pixbuf->pixels = NULL; pixbuf->destroy = NULL; pixbuf->destroy_data = NULL; if (destroy) destroy (destroy_data, pixels); art_free (pixbuf); } /** * art_pixbuf_free_shallow: * @pixbuf: The #ArtPixBuf to be destroyed. * * Destroys the #ArtPixBuf without calling the destroy notification function. * * This function is deprecated. Use the _dnotify variants for * allocation instead. **/ void art_pixbuf_free_shallow (ArtPixBuf *pixbuf) { art_free (pixbuf); } /** * art_pixbuf_duplicate: Duplicate a pixbuf. * @pixbuf: The #ArtPixBuf to duplicate. * * Duplicates a pixbuf, including duplicating the buffer. * * Return value: The duplicated pixbuf. **/ ArtPixBuf * art_pixbuf_duplicate (const ArtPixBuf *pixbuf) { ArtPixBuf *result; int size; result = art_new (ArtPixBuf, 1); result->format = pixbuf->format; result->n_channels = pixbuf->n_channels; result->has_alpha = pixbuf->has_alpha; result->bits_per_sample = pixbuf->bits_per_sample; size = (pixbuf->height - 1) * pixbuf->rowstride + pixbuf->width * ((pixbuf->n_channels * pixbuf->bits_per_sample + 7) >> 3); result->pixels = art_alloc (size); memcpy (result->pixels, pixbuf->pixels, size); result->width = pixbuf->width; result->height = pixbuf->height; result->rowstride = pixbuf->rowstride; result->destroy_data = NULL; result->destroy = art_pixel_destroy; return result; } rl-renderpm-4.0.3/src/libart_lgpl/art_pixbuf.h000066400000000000000000000055711453236046100213570ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_PIXBUF_H__ #define __ART_PIXBUF_H__ /* A generic data structure for holding a buffer of pixels. One way to think about this module is as a virtualization over specific pixel buffer formats. */ #ifdef LIBART_COMPILATION #include "art_misc.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif typedef void (*ArtDestroyNotify) (void *func_data, void *data); typedef struct _ArtPixBuf ArtPixBuf; typedef enum { ART_PIX_RGB /* gray, cmyk, lab, ... ? */ } ArtPixFormat; /* The pixel buffer consists of width * height pixels, each of which has n_channels samples. It is stored in simple packed format. */ struct _ArtPixBuf { /*< public >*/ ArtPixFormat format; int n_channels; int has_alpha; int bits_per_sample; art_u8 *pixels; int width; int height; int rowstride; void *destroy_data; ArtDestroyNotify destroy; }; /* allocate an ArtPixBuf from art_alloc()ed pixels (automated destruction) */ ArtPixBuf * art_pixbuf_new_rgb (art_u8 *pixels, int width, int height, int rowstride); ArtPixBuf * art_pixbuf_new_rgba (art_u8 *pixels, int width, int height, int rowstride); /* allocate an ArtPixBuf from constant pixels (no destruction) */ ArtPixBuf * art_pixbuf_new_const_rgb (const art_u8 *pixels, int width, int height, int rowstride); ArtPixBuf * art_pixbuf_new_const_rgba (const art_u8 *pixels, int width, int height, int rowstride); /* allocate an ArtPixBuf and notify creator upon destruction */ ArtPixBuf * art_pixbuf_new_rgb_dnotify (art_u8 *pixels, int width, int height, int rowstride, void *dfunc_data, ArtDestroyNotify dfunc); ArtPixBuf * art_pixbuf_new_rgba_dnotify (art_u8 *pixels, int width, int height, int rowstride, void *dfunc_data, ArtDestroyNotify dfunc); /* free an ArtPixBuf with destroy notification */ void art_pixbuf_free (ArtPixBuf *pixbuf); /* deprecated function, use the _dnotify variants for allocation instead */ void art_pixbuf_free_shallow (ArtPixBuf *pixbuf); ArtPixBuf * art_pixbuf_duplicate (const ArtPixBuf *pixbuf); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_point.h000066400000000000000000000021261453236046100212040ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_POINT_H__ #define __ART_POINT_H__ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtPoint ArtPoint; struct _ArtPoint { /*< public >*/ double x, y; }; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_POINT_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_rect.c000066400000000000000000000143341453236046100210070ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rect.h" #include #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /* MAX */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif /* MIN */ /* rectangle primitives stolen from gzilla */ /** * art_irect_copy: Make a copy of an integer rectangle. * @dest: Where the copy is stored. * @src: The source rectangle. * * Copies the rectangle. **/ void art_irect_copy (ArtIRect *dest, const ArtIRect *src) { dest->x0 = src->x0; dest->y0 = src->y0; dest->x1 = src->x1; dest->y1 = src->y1; } /** * art_irect_union: Find union of two integer rectangles. * @dest: Where the result is stored. * @src1: A source rectangle. * @src2: Another source rectangle. * * Finds the smallest rectangle that includes @src1 and @src2. **/ void art_irect_union (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2) { if (art_irect_empty (src1)) { art_irect_copy (dest, src2); } else if (art_irect_empty (src2)) { art_irect_copy (dest, src1); } else { dest->x0 = MIN (src1->x0, src2->x0); dest->y0 = MIN (src1->y0, src2->y0); dest->x1 = MAX (src1->x1, src2->x1); dest->y1 = MAX (src1->y1, src2->y1); } } /** * art_irect_intersection: Find intersection of two integer rectangles. * @dest: Where the result is stored. * @src1: A source rectangle. * @src2: Another source rectangle. * * Finds the intersection of @src1 and @src2. **/ void art_irect_intersect (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2) { dest->x0 = MAX (src1->x0, src2->x0); dest->y0 = MAX (src1->y0, src2->y0); dest->x1 = MIN (src1->x1, src2->x1); dest->y1 = MIN (src1->y1, src2->y1); } /** * art_irect_empty: Determine whether integer rectangle is empty. * @src: The source rectangle. * * Return value: TRUE if @src is an empty rectangle, FALSE otherwise. **/ int art_irect_empty (const ArtIRect *src) { return (src->x1 <= src->x0 || src->y1 <= src->y0); } #if 0 gboolean irect_point_inside (ArtIRect *rect, GzwPoint *point) { return (point->x >= rect->x0 && point->y >= rect->y0 && point->x < rect->x1 && point->y < rect->y1); } #endif /** * art_drect_copy: Make a copy of a rectangle. * @dest: Where the copy is stored. * @src: The source rectangle. * * Copies the rectangle. **/ void art_drect_copy (ArtDRect *dest, const ArtDRect *src) { dest->x0 = src->x0; dest->y0 = src->y0; dest->x1 = src->x1; dest->y1 = src->y1; } /** * art_drect_union: Find union of two rectangles. * @dest: Where the result is stored. * @src1: A source rectangle. * @src2: Another source rectangle. * * Finds the smallest rectangle that includes @src1 and @src2. **/ void art_drect_union (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2) { if (art_drect_empty (src1)) { art_drect_copy (dest, src2); } else if (art_drect_empty (src2)) { art_drect_copy (dest, src1); } else { dest->x0 = MIN (src1->x0, src2->x0); dest->y0 = MIN (src1->y0, src2->y0); dest->x1 = MAX (src1->x1, src2->x1); dest->y1 = MAX (src1->y1, src2->y1); } } /** * art_drect_intersection: Find intersection of two rectangles. * @dest: Where the result is stored. * @src1: A source rectangle. * @src2: Another source rectangle. * * Finds the intersection of @src1 and @src2. **/ void art_drect_intersect (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2) { dest->x0 = MAX (src1->x0, src2->x0); dest->y0 = MAX (src1->y0, src2->y0); dest->x1 = MIN (src1->x1, src2->x1); dest->y1 = MIN (src1->y1, src2->y1); } /** * art_irect_empty: Determine whether rectangle is empty. * @src: The source rectangle. * * Return value: TRUE if @src is an empty rectangle, FALSE otherwise. **/ int art_drect_empty (const ArtDRect *src) { return (src->x1 <= src->x0 || src->y1 <= src->y0); } /** * art_drect_affine_transform: Affine transform rectangle. * @dst: Where to store the result. * @src: The source rectangle. * @matrix: The affine transformation. * * Find the smallest rectangle enclosing the affine transformed @src. * The result is exactly the affine transformation of @src when * @matrix specifies a rectilinear affine transformation, otherwise it * is a conservative approximation. **/ void art_drect_affine_transform (ArtDRect *dst, const ArtDRect *src, const double matrix[6]) { double x00, y00, x10, y10; double x01, y01, x11, y11; x00 = src->x0 * matrix[0] + src->y0 * matrix[2] + matrix[4]; y00 = src->x0 * matrix[1] + src->y0 * matrix[3] + matrix[5]; x10 = src->x1 * matrix[0] + src->y0 * matrix[2] + matrix[4]; y10 = src->x1 * matrix[1] + src->y0 * matrix[3] + matrix[5]; x01 = src->x0 * matrix[0] + src->y1 * matrix[2] + matrix[4]; y01 = src->x0 * matrix[1] + src->y1 * matrix[3] + matrix[5]; x11 = src->x1 * matrix[0] + src->y1 * matrix[2] + matrix[4]; y11 = src->x1 * matrix[1] + src->y1 * matrix[3] + matrix[5]; dst->x0 = MIN (MIN (x00, x10), MIN (x01, x11)); dst->y0 = MIN (MIN (y00, y10), MIN (y01, y11)); dst->x1 = MAX (MAX (x00, x10), MAX (x01, x11)); dst->y1 = MAX (MAX (y00, y10), MAX (y01, y11)); } /** * art_drect_to_irect: Convert rectangle to integer rectangle. * @dst: Where to store resulting integer rectangle. * @src: The source rectangle. * * Find the smallest integer rectangle that encloses @src. **/ void art_drect_to_irect (ArtIRect *dst, ArtDRect *src) { dst->x0 = (int) floor (src->x0); dst->y0 = (int) floor (src->y0); dst->x1 = (int) ceil (src->x1); dst->y1 = (int) ceil (src->y1); } rl-renderpm-4.0.3/src/libart_lgpl/art_rect.h000066400000000000000000000043771453236046100210220ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RECT_H__ #define __ART_RECT_H__ #ifdef __cplusplus extern "C" { #endif typedef struct _ArtDRect ArtDRect; typedef struct _ArtIRect ArtIRect; struct _ArtDRect { /*< public >*/ double x0, y0, x1, y1; }; struct _ArtIRect { /*< public >*/ int x0, y0, x1, y1; }; /* Make a copy of the rectangle. */ void art_irect_copy (ArtIRect *dest, const ArtIRect *src); /* Find the smallest rectangle that includes both source rectangles. */ void art_irect_union (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2); /* Return the intersection of the two rectangles */ void art_irect_intersect (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2); /* Return true if the rectangle is empty. */ int art_irect_empty (const ArtIRect *src); /* Make a copy of the rectangle. */ void art_drect_copy (ArtDRect *dest, const ArtDRect *src); /* Find the smallest rectangle that includes both source rectangles. */ void art_drect_union (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2); /* Return the intersection of the two rectangles */ void art_drect_intersect (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2); /* Return true if the rectangle is empty. */ int art_drect_empty (const ArtDRect *src); void art_drect_affine_transform (ArtDRect *dst, const ArtDRect *src, const double matrix[6]); void art_drect_to_irect (ArtIRect *dst, ArtDRect *src); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rect_svp.c000066400000000000000000000043031453236046100216720ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rect_svp.h" #include "art_misc.h" #include "art_svp.h" #include "art_rect.h" #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /* MAX */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif /* MIN */ /** * art_drect_svp: Find the bounding box of a sorted vector path. * @bbox: Where to store the bounding box. * @svp: The SVP. * * Finds the bounding box of the SVP. **/ void art_drect_svp (ArtDRect *bbox, const ArtSVP *svp) { int i; if (svp->n_segs == 0) { bbox->x0 = 0; bbox->y0 = 0; bbox->x1 = 0; bbox->y1 = 0; return; } art_drect_copy (bbox, &svp->segs[0].bbox); for (i = 1; i < svp->n_segs; i++) { bbox->x0 = MIN (bbox->x0, svp->segs[i].bbox.x0); bbox->y0 = MIN (bbox->y0, svp->segs[i].bbox.y0); bbox->x1 = MAX (bbox->x1, svp->segs[i].bbox.x1); bbox->y1 = MAX (bbox->y1, svp->segs[i].bbox.y1); } } /** * art_drect_svp_union: Compute the bounding box of the svp and union it in to the existing bounding box. * @bbox: Initial boundin box and where to store the bounding box. * @svp: The SVP. * * Finds the bounding box of the SVP, computing its union with an * existing bbox. **/ void art_drect_svp_union (ArtDRect *bbox, const ArtSVP *svp) { ArtDRect svp_bbox; art_drect_svp (&svp_bbox, svp); art_drect_union (bbox, bbox, &svp_bbox); } rl-renderpm-4.0.3/src/libart_lgpl/art_rect_svp.h000066400000000000000000000025531453236046100217040ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RECT_SVP_H__ #define __ART_RECT_SVP_H__ /* Find the bounding box of a sorted vector path. */ #ifdef LIBART_COMPILATION #include "art_svp.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void art_drect_svp (ArtDRect *bbox, const ArtSVP *svp); /* Compute the bounding box of the svp and union it in to the existing bounding box. */ void art_drect_svp_union (ArtDRect *bbox, const ArtSVP *svp); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_RECT_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_rect_uta.c000066400000000000000000000075271453236046100216660ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rect_uta.h" /* Functions to decompose a microtile array into a list of rectangles. */ /** * art_rect_list_from_uta: Decompose uta into list of rectangles. * @uta: The source uta. * @max_width: The maximum width of the resulting rectangles. * @max_height: The maximum height of the resulting rectangles. * @p_nrects: Where to store the number of returned rectangles. * * Allocates a new list of rectangles, sets *@p_nrects to the number * in the list. This list should be freed with art_free(). * * Each rectangle bounded in size by (@max_width, @max_height). * However, these bounds must be at least the size of one tile. * * This routine provides a precise implementation, i.e. the rectangles * cover exactly the same area as the uta. It is thus appropriate in * cases where the overhead per rectangle is small compared with the * cost of filling in extra pixels. * * Return value: An array containing the resulting rectangles. **/ ArtIRect * art_rect_list_from_uta (ArtUta *uta, int max_width, int max_height, int *p_nrects) { ArtIRect *rects; int n_rects, n_rects_max; int x, y; int width, height; int ix; int left_ix; ArtUtaBbox *utiles; ArtUtaBbox bb; int x0, y0, x1, y1; int *glom; int glom_rect; n_rects = 0; n_rects_max = 1; rects = art_new (ArtIRect, n_rects_max); width = uta->width; height = uta->height; utiles = uta->utiles; glom = art_new (int, width * height); for (ix = 0; ix < width * height; ix++) glom[ix] = -1; ix = 0; for (y = 0; y < height; y++) for (x = 0; x < width; x++) { bb = utiles[ix]; if (bb) { x0 = ((uta->x0 + x) << ART_UTILE_SHIFT) + ART_UTA_BBOX_X0(bb); y0 = ((uta->y0 + y) << ART_UTILE_SHIFT) + ART_UTA_BBOX_Y0(bb); y1 = ((uta->y0 + y) << ART_UTILE_SHIFT) + ART_UTA_BBOX_Y1(bb); left_ix = ix; /* now try to extend to the right */ while (x != width - 1 && ART_UTA_BBOX_X1(bb) == ART_UTILE_SIZE && (((bb & 0xffffff) ^ utiles[ix + 1]) & 0xffff00ff) == 0 && (((uta->x0 + x + 1) << ART_UTILE_SHIFT) + ART_UTA_BBOX_X1(utiles[ix + 1]) - x0) <= (unsigned) max_width) { bb = utiles[ix + 1]; ix++; x++; } x1 = ((uta->x0 + x) << ART_UTILE_SHIFT) + ART_UTA_BBOX_X1(bb); /* if rectangle nonempty */ if ((x1 ^ x0) | (y1 ^ y0)) { /* try to glom onto an existing rectangle */ glom_rect = glom[left_ix]; if (glom_rect != -1 && x0 == rects[glom_rect].x0 && x1 == rects[glom_rect].x1 && y0 == rects[glom_rect].y1 && y1 - rects[glom_rect].y0 <= max_height) { rects[glom_rect].y1 = y1; } else { if (n_rects == n_rects_max) art_expand (rects, ArtIRect, n_rects_max); rects[n_rects].x0 = x0; rects[n_rects].y0 = y0; rects[n_rects].x1 = x1; rects[n_rects].y1 = y1; glom_rect = n_rects; n_rects++; } if (y != height - 1) glom[left_ix + width] = glom_rect; } } ix++; } art_free (glom); *p_nrects = n_rects; return rects; } rl-renderpm-4.0.3/src/libart_lgpl/art_rect_uta.h000066400000000000000000000023751453236046100216670ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RECT_UTA_H__ #define __ART_RECT_UTA_H__ #ifdef LIBART_COMPILATION #include "art_rect.h" #include "art_uta.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtIRect * art_rect_list_from_uta (ArtUta *uta, int max_width, int max_height, int *p_nrects); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_RECT_UTA_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_render.c000066400000000000000000001055311453236046100213310ustar00rootroot00000000000000/* * art_render.c: Modular rendering architecture. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_render.h" #include "art_rgb.h" typedef struct _ArtRenderPriv ArtRenderPriv; struct _ArtRenderPriv { ArtRender super; ArtImageSource *image_source; int n_mask_source; ArtMaskSource **mask_source; int n_callbacks; ArtRenderCallback **callbacks; }; ArtRender * art_render_new (int x0, int y0, int x1, int y1, art_u8 *pixels, int rowstride, int n_chan, int depth, ArtAlphaType alpha_type, ArtAlphaGamma *alphagamma) { ArtRenderPriv *priv; ArtRender *result; priv = art_new (ArtRenderPriv, 1); result = &priv->super; if (n_chan > ART_MAX_CHAN) { art_warn ("art_render_new: n_chan = %d, exceeds %d max\n", n_chan, ART_MAX_CHAN); return NULL; } if (depth > ART_MAX_DEPTH) { art_warn ("art_render_new: depth = %d, exceeds %d max\n", depth, ART_MAX_DEPTH); return NULL; } if (x0 >= x1) { art_warn ("art_render_new: x0 >= x1 (x0 = %d, x1 = %d)\n", x0, x1); return NULL; } result->x0 = x0; result->y0 = y0; result->x1 = x1; result->y1 = y1; result->pixels = pixels; result->rowstride = rowstride; result->n_chan = n_chan; result->depth = depth; result->alpha_type = alpha_type; result->clear = ART_FALSE; result->opacity = 0x10000; result->compositing_mode = ART_COMPOSITE_NORMAL; result->alphagamma = alphagamma; result->alpha_buf = NULL; result->image_buf = NULL; result->run = NULL; result->span_x = NULL; result->need_span = ART_FALSE; priv->image_source = NULL; priv->n_mask_source = 0; priv->mask_source = NULL; return result; } /* todo on clear routines: I haven't really figured out what to do with clearing the alpha channel. It _should_ be possible to clear to an arbitrary RGBA color. */ /** * art_render_clear: Set clear color. * @clear_color: Color with which to clear dest. * * Sets clear color, equivalent to actually clearing the destination * buffer before rendering. This is the most general form. **/ void art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color) { int i; int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); render->clear = ART_TRUE; for (i = 0; i < n_ch; i++) render->clear_color[i] = clear_color[i]; } /** * art_render_clear_rgb: Set clear color, given in RGB format. * @clear_rgb: Clear color, in 0xRRGGBB format. * * Sets clear color, equivalent to actually clearing the destination * buffer before rendering. **/ void art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb) { if (render->n_chan != 3) art_warn ("art_render_clear_rgb: called on render with %d channels, only works with 3\n", render->n_chan); else { int r, g, b; render->clear = ART_TRUE; r = clear_rgb >> 16; g = (clear_rgb >> 8) & 0xff; b = clear_rgb & 0xff; render->clear_color[0] = ART_PIX_MAX_FROM_8(r); render->clear_color[1] = ART_PIX_MAX_FROM_8(g); render->clear_color[2] = ART_PIX_MAX_FROM_8(b); } } static void art_render_nop_done (ArtRenderCallback *self, ArtRender *render) { } static void art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { int width = render->x1 - render->x0; art_u8 r, g, b; ArtPixMaxDepth color_max; color_max = render->clear_color[0]; r = ART_PIX_8_FROM_MAX (color_max); color_max = render->clear_color[1]; g = ART_PIX_8_FROM_MAX (color_max); color_max = render->clear_color[2]; b = ART_PIX_8_FROM_MAX (color_max); art_rgb_fill_run (dest, r, g, b, width); } static void art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { int width = render->x1 - render->x0; int i, j; int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); int ix; art_u8 color[ART_MAX_CHAN + 1]; for (j = 0; j < n_ch; j++) { ArtPixMaxDepth color_max = render->clear_color[j]; color[j] = ART_PIX_8_FROM_MAX (color_max); } ix = 0; for (i = 0; i < width; i++) for (j = 0; j < n_ch; j++) dest[ix++] = color[j]; } const ArtRenderCallback art_render_clear_rgb8_obj = { art_render_clear_render_rgb8, art_render_nop_done }; const ArtRenderCallback art_render_clear_8_obj = { art_render_clear_render_8, art_render_nop_done }; #if ART_MAX_DEPTH >= 16 static void art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { int width = render->x1 - render->x0; int i, j; int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); int ix; art_u16 *dest_16 = (art_u16 *)dest; art_u8 color[ART_MAX_CHAN + 1]; for (j = 0; j < n_ch; j++) { int color_16 = render->clear_color[j]; color[j] = color_16; } ix = 0; for (i = 0; i < width; i++) for (j = 0; j < n_ch; j++) dest_16[ix++] = color[j]; } const ArtRenderCallback art_render_clear_16_obj = { art_render_clear_render_16, art_render_nop_done }; #endif /* ART_MAX_DEPTH >= 16 */ /* todo: inline */ static ArtRenderCallback * art_render_choose_clear_callback (ArtRender *render) { ArtRenderCallback *clear_callback; if (render->depth == 8) { if (render->n_chan == 3 && render->alpha_type == ART_ALPHA_NONE) clear_callback = (ArtRenderCallback *)&art_render_clear_rgb8_obj; else clear_callback = (ArtRenderCallback *)&art_render_clear_8_obj; } #if ART_MAX_DEPTH >= 16 else if (render->depth == 16) clear_callback = (ArtRenderCallback *)&art_render_clear_16_obj; #endif else { art_die ("art_render_choose_clear_callback: inconsistent render->depth = %d\n", render->depth); } return clear_callback; } #if 0 /* todo: get around to writing this */ static void art_render_composite_render_noa_8_norm (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { int width = render->x1 - render->x0; } #endif /* This is the most general form of the function. It is slow but (hopefully) correct. Actually, I'm still worried about roundoff errors in the premul case - it seems to me that an off-by-one could lead to overflow. */ static void art_render_composite (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtRenderMaskRun *run = render->run; art_u32 depth = render->depth; int n_run = render->n_run; int x0 = render->x0; int x; int run_x0, run_x1; art_u8 *alpha_buf = render->alpha_buf; art_u8 *image_buf = render->image_buf; int i, j; art_u32 tmp; art_u32 run_alpha; art_u32 alpha; int image_ix; art_u16 src[ART_MAX_CHAN + 1]; art_u16 dst[ART_MAX_CHAN + 1]; int n_chan = render->n_chan; ArtAlphaType alpha_type = render->alpha_type; int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE); int dst_pixstride = n_ch * (depth >> 3); int buf_depth = render->buf_depth; ArtAlphaType buf_alpha = render->buf_alpha; int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE); int buf_pixstride = buf_n_ch * (buf_depth >> 3); art_u8 *bufptr; art_u32 src_alpha; art_u32 src_mul; art_u8 *dstptr; art_u32 dst_alpha; art_u32 dst_mul; image_ix = 0; for (i = 0; i < n_run - 1; i++) { run_x0 = run[i].x; run_x1 = run[i + 1].x; tmp = run[i].alpha; if (tmp < 0x8100) continue; run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ bufptr = image_buf + (run_x0 - x0) * buf_pixstride; dstptr = dest + (run_x0 - x0) * dst_pixstride; for (x = run_x0; x < run_x1; x++) { if (alpha_buf) { if (depth == 8) { tmp = run_alpha * alpha_buf[x - x0] + 0x80; /* range 0x80 .. 0xff0080 */ alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; } else /* (depth == 16) */ { tmp = ((art_u16 *)alpha_buf)[x - x0]; tmp = (run_alpha * tmp + 0x8000) >> 8; /* range 0x80 .. 0xffff80 */ alpha = (tmp + (tmp >> 16)) >> 8; } } else alpha = run_alpha; /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */ /* convert (src pixel * alpha) to premul alpha form, store in src as 0..0xffff range */ if (buf_alpha == ART_ALPHA_NONE) { src_alpha = alpha; src_mul = src_alpha; } else { if (buf_depth == 8) { tmp = alpha * bufptr[n_chan] + 0x80; /* range 0x80 .. 0xff0080 */ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; } else /* (depth == 16) */ { tmp = ((art_u16 *)bufptr)[n_chan]; tmp = (alpha * tmp + 0x8000) >> 8; /* range 0x80 .. 0xffff80 */ src_alpha = (tmp + (tmp >> 16)) >> 8; } if (buf_alpha == ART_ALPHA_SEPARATE) src_mul = src_alpha; else /* buf_alpha == (ART_ALPHA_PREMUL) */ src_mul = alpha; } /* src_alpha is the (alpha of the source pixel * alpha), range 0..0x10000 */ if (buf_depth == 8) { src_mul *= 0x101; for (j = 0; j < n_chan; j++) src[j] = (bufptr[j] * src_mul + 0x8000) >> 16; } else if (buf_depth == 16) { for (j = 0; j < n_chan; j++) src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16; } bufptr += buf_pixstride; /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range 0..0x10000) now contain the source pixel with premultiplied alpha */ /* convert dst pixel to premul alpha form, store in dst as 0..0xffff range */ if (alpha_type == ART_ALPHA_NONE) { dst_alpha = 0x10000; dst_mul = dst_alpha; } else { if (depth == 8) { tmp = dstptr[n_chan]; /* range 0..0xff */ dst_alpha = (tmp << 8) + tmp + (tmp >> 7); } else /* (depth == 16) */ { tmp = ((art_u16 *)bufptr)[n_chan]; dst_alpha = (tmp + (tmp >> 15)); } if (alpha_type == ART_ALPHA_SEPARATE) dst_mul = dst_alpha; else /* (alpha_type == ART_ALPHA_PREMUL) */ dst_mul = 0x10000; } /* dst_alpha is the alpha of the dest pixel, range 0..0x10000 */ if (depth == 8) { dst_mul *= 0x101; for (j = 0; j < n_chan; j++) dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16; } else if (buf_depth == 16) { for (j = 0; j < n_chan; j++) dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16; } /* do the compositing, dst = (src over dst) */ for (j = 0; j < n_chan; j++) { art_u32 srcv, dstv; art_u32 tmp; srcv = src[j]; dstv = dst[j]; tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv; tmp -= tmp >> 16; dst[j] = tmp; } if (alpha_type == ART_ALPHA_NONE) { if (depth == 8) dst_mul = 0xff; else /* (depth == 16) */ dst_mul = 0xffff; } else { if (src_alpha >= 0x10000) dst_alpha = 0x10000; else dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0) { if (depth == 8) dst_mul = 0xff; else /* (depth == 16) */ dst_mul = 0xffff; } else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */ { if (depth == 8) dst_mul = 0xff0000 / dst_alpha; else /* (depth == 16) */ dst_mul = 0xffff0000 / dst_alpha; } } if (depth == 8) { for (j = 0; j < n_chan; j++) dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16; if (alpha_type != ART_ALPHA_NONE) dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16; } else if (depth == 16) { for (j = 0; j < n_chan; j++) ((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16; if (alpha_type != ART_ALPHA_NONE) dstptr[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16; } dstptr += dst_pixstride; } } } const ArtRenderCallback art_render_composite_obj = { art_render_composite, art_render_nop_done }; static void art_render_composite_8 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtRenderMaskRun *run = render->run; int n_run = render->n_run; int x0 = render->x0; int x; int run_x0, run_x1; art_u8 *alpha_buf = render->alpha_buf; art_u8 *image_buf = render->image_buf; int i, j; art_u32 tmp; art_u32 run_alpha; art_u32 alpha; int image_ix; int n_chan = render->n_chan; ArtAlphaType alpha_type = render->alpha_type; int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE); int dst_pixstride = n_ch; ArtAlphaType buf_alpha = render->buf_alpha; int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE); int buf_pixstride = buf_n_ch; art_u8 *bufptr; art_u32 src_alpha; art_u32 src_mul; art_u8 *dstptr; art_u32 dst_alpha; art_u32 dst_mul, dst_save_mul; image_ix = 0; for (i = 0; i < n_run - 1; i++) { run_x0 = run[i].x; run_x1 = run[i + 1].x; tmp = run[i].alpha; if (tmp < 0x10000) continue; run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ bufptr = image_buf + (run_x0 - x0) * buf_pixstride; dstptr = dest + (run_x0 - x0) * dst_pixstride; for (x = run_x0; x < run_x1; x++) { if (alpha_buf) { tmp = run_alpha * alpha_buf[x - x0] + 0x80; /* range 0x80 .. 0xff0080 */ alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; } else alpha = run_alpha; /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */ /* convert (src pixel * alpha) to premul alpha form, store in src as 0..0xffff range */ if (buf_alpha == ART_ALPHA_NONE) { src_alpha = alpha; src_mul = src_alpha; } else { tmp = alpha * bufptr[n_chan] + 0x80; /* range 0x80 .. 0xff0080 */ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; if (buf_alpha == ART_ALPHA_SEPARATE) src_mul = src_alpha; else /* buf_alpha == (ART_ALPHA_PREMUL) */ src_mul = alpha; } /* src_alpha is the (alpha of the source pixel * alpha), range 0..0x10000 */ src_mul *= 0x101; if (alpha_type == ART_ALPHA_NONE) { dst_alpha = 0x10000; dst_mul = dst_alpha; } else { tmp = dstptr[n_chan]; /* range 0..0xff */ dst_alpha = (tmp << 8) + tmp + (tmp >> 7); if (alpha_type == ART_ALPHA_SEPARATE) dst_mul = dst_alpha; else /* (alpha_type == ART_ALPHA_PREMUL) */ dst_mul = 0x10000; } /* dst_alpha is the alpha of the dest pixel, range 0..0x10000 */ dst_mul *= 0x101; if (alpha_type == ART_ALPHA_NONE) { dst_save_mul = 0xff; } else { if (src_alpha >= 0x10000) dst_alpha = 0x10000; else dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0) { dst_save_mul = 0xff; } else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */ { dst_save_mul = 0xff0000 / dst_alpha; } } for (j = 0; j < n_chan; j++) { art_u32 src, dst; art_u32 tmp; src = (bufptr[j] * src_mul + 0x8000) >> 16; dst = (dstptr[j] * dst_mul + 0x8000) >> 16; tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src; tmp -= tmp >> 16; dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; } if (alpha_type != ART_ALPHA_NONE) dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16; bufptr += buf_pixstride; dstptr += dst_pixstride; } } } const ArtRenderCallback art_render_composite_8_obj = { art_render_composite_8, art_render_nop_done }; /* Assumes: * alpha_buf is NULL * buf_alpha = ART_ALPHA_NONE (source) * alpha_type = ART_ALPHA_SEPARATE (dest) * n_chan = 3; */ static void art_render_composite_8_opt1 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtRenderMaskRun *run = render->run; int n_run = render->n_run; int x0 = render->x0; int x; int run_x0, run_x1; art_u8 *image_buf = render->image_buf; int i, j; art_u32 tmp; art_u32 run_alpha; int image_ix; art_u8 *bufptr; art_u32 src_mul; art_u8 *dstptr; art_u32 dst_alpha; art_u32 dst_mul, dst_save_mul; image_ix = 0; for (i = 0; i < n_run - 1; i++) { run_x0 = run[i].x; run_x1 = run[i + 1].x; tmp = run[i].alpha; if (tmp < 0x10000) continue; run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ bufptr = image_buf + (run_x0 - x0) * 3; dstptr = dest + (run_x0 - x0) * 4; if (run_alpha == 0x10000) { for (x = run_x0; x < run_x1; x++) { *dstptr++ = *bufptr++; *dstptr++ = *bufptr++; *dstptr++ = *bufptr++; *dstptr++ = 0xff; } } else { for (x = run_x0; x < run_x1; x++) { src_mul = run_alpha * 0x101; tmp = dstptr[3]; /* range 0..0xff */ dst_alpha = (tmp << 8) + tmp + (tmp >> 7); dst_mul = dst_alpha; /* dst_alpha is the alpha of the dest pixel, range 0..0x10000 */ dst_mul *= 0x101; dst_alpha += ((((0x10000 - dst_alpha) * run_alpha) >> 8) + 0x80) >> 8; if (dst_alpha == 0) dst_save_mul = 0xff; else /* (dst_alpha != 0) */ dst_save_mul = 0xff0000 / dst_alpha; for (j = 0; j < 3; j++) { art_u32 src, dst; art_u32 tmp; src = (bufptr[j] * src_mul + 0x8000) >> 16; dst = (dstptr[j] * dst_mul + 0x8000) >> 16; tmp = ((dst * (0x10000 - run_alpha) + 0x8000) >> 16) + src; tmp -= tmp >> 16; dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; } dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16; bufptr += 3; dstptr += 4; } } } } const ArtRenderCallback art_render_composite_8_opt1_obj = { art_render_composite_8_opt1, art_render_nop_done }; /* Assumes: * alpha_buf is NULL * buf_alpha = ART_ALPHA_PREMUL (source) * alpha_type = ART_ALPHA_SEPARATE (dest) * n_chan = 3; */ static void art_render_composite_8_opt2 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtRenderMaskRun *run = render->run; int n_run = render->n_run; int x0 = render->x0; int x; int run_x0, run_x1; art_u8 *image_buf = render->image_buf; int i, j; art_u32 tmp; art_u32 run_alpha; int image_ix; art_u8 *bufptr; art_u32 src_alpha; art_u32 src_mul; art_u8 *dstptr; art_u32 dst_alpha; art_u32 dst_mul, dst_save_mul; image_ix = 0; for (i = 0; i < n_run - 1; i++) { run_x0 = run[i].x; run_x1 = run[i + 1].x; tmp = run[i].alpha; if (tmp < 0x10000) continue; run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ bufptr = image_buf + (run_x0 - x0) * 4; dstptr = dest + (run_x0 - x0) * 4; if (run_alpha == 0x10000) { for (x = run_x0; x < run_x1; x++) { src_alpha = (bufptr[3] << 8) + bufptr[3] + (bufptr[3] >> 7); /* src_alpha is the (alpha of the source pixel), range 0..0x10000 */ dst_alpha = (dstptr[3] << 8) + dstptr[3] + (dstptr[3] >> 7); /* dst_alpha is the alpha of the dest pixel, range 0..0x10000 */ dst_mul = dst_alpha*0x101; if (src_alpha >= 0x10000) dst_alpha = 0x10000; else dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; if (dst_alpha == 0) dst_save_mul = 0xff; else /* dst_alpha != 0) */ dst_save_mul = 0xff0000 / dst_alpha; for (j = 0; j < 3; j++) { art_u32 src, dst; art_u32 tmp; src = (bufptr[j] << 8) | bufptr[j]; dst = (dstptr[j] * dst_mul + 0x8000) >> 16; tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src; tmp -= tmp >> 16; dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; } dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16; bufptr += 4; dstptr += 4; } } else { for (x = run_x0; x < run_x1; x++) { tmp = run_alpha * bufptr[3] + 0x80; /* range 0x80 .. 0xff0080 */ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; /* src_alpha is the (alpha of the source pixel * alpha), range 0..0x10000 */ src_mul = run_alpha * 0x101; tmp = dstptr[3]; /* range 0..0xff */ dst_alpha = (tmp << 8) + tmp + (tmp >> 7); dst_mul = dst_alpha; /* dst_alpha is the alpha of the dest pixel, range 0..0x10000 */ dst_mul *= 0x101; if (src_alpha >= 0x10000) dst_alpha = 0x10000; else dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; if (dst_alpha == 0) { dst_save_mul = 0xff; } else /* dst_alpha != 0) */ { dst_save_mul = 0xff0000 / dst_alpha; } for (j = 0; j < 3; j++) { art_u32 src, dst; art_u32 tmp; src = (bufptr[j] * src_mul + 0x8000) >> 16; dst = (dstptr[j] * dst_mul + 0x8000) >> 16; tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src; tmp -= tmp >> 16; dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; } dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16; bufptr += 4; dstptr += 4; } } } } const ArtRenderCallback art_render_composite_8_opt2_obj = { art_render_composite_8_opt2, art_render_nop_done }; /* todo: inline */ static ArtRenderCallback * art_render_choose_compositing_callback (ArtRender *render) { if (render->depth == 8 && render->buf_depth == 8) { if (render->n_chan == 3 && render->alpha_buf == NULL && render->alpha_type == ART_ALPHA_SEPARATE) { if (render->buf_alpha == ART_ALPHA_NONE) return (ArtRenderCallback *)&art_render_composite_8_opt1_obj; else if (render->buf_alpha == ART_ALPHA_PREMUL) return (ArtRenderCallback *)&art_render_composite_8_opt2_obj; } return (ArtRenderCallback *)&art_render_composite_8_obj; } return (ArtRenderCallback *)&art_render_composite_obj; } /** * art_render_invoke_callbacks: Invoke the callbacks in the render object. * @render: The render object. * @y: The current Y coordinate value. * * Invokes the callbacks of the render object in the appropriate * order. Drivers should call this routine once per scanline. * * todo: should management of dest devolve to this routine? very * plausibly yes. **/ void art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y) { ArtRenderPriv *priv = (ArtRenderPriv *)render; int i; for (i = 0; i < priv->n_callbacks; i++) { ArtRenderCallback *callback; callback = priv->callbacks[i]; callback->render (callback, render, dest, y); } } /** * art_render_invoke: Perform the requested rendering task. * @render: The render object. * * Invokes the renderer and all sources associated with it, to perform * the requested rendering task. **/ void art_render_invoke (ArtRender *render) { ArtRenderPriv *priv = (ArtRenderPriv *)render; int width; int best_driver, best_score; int i; int n_callbacks, n_callbacks_max; ArtImageSource *image_source; ArtImageSourceFlags image_flags; int buf_depth; ArtAlphaType buf_alpha; art_boolean first = ART_TRUE; if (render == NULL) { art_warn ("art_render_invoke: called with render == NULL\n"); return; } if (priv->image_source == NULL) { art_warn ("art_render_invoke: no image source given\n"); return; } width = render->x1 - render->x0; render->run = art_new (ArtRenderMaskRun, width + 1); /* Elect a mask source as driver. */ best_driver = -1; best_score = 0; for (i = 0; i < priv->n_mask_source; i++) { int score; ArtMaskSource *mask_source; mask_source = priv->mask_source[i]; score = mask_source->can_drive (mask_source, render); if (score > best_score) { best_score = score; best_driver = i; } } /* Allocate alpha buffer if needed. */ if (priv->n_mask_source > 1 || (priv->n_mask_source == 1 && best_driver < 0)) { render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3); } /* Negotiate image rendering and compositing. */ image_source = priv->image_source; image_source->negotiate (image_source, render, &image_flags, &buf_depth, &buf_alpha); /* Build callback list. */ n_callbacks_max = priv->n_mask_source + 3; priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max); n_callbacks = 0; for (i = 0; i < priv->n_mask_source; i++) if (i != best_driver) { ArtMaskSource *mask_source = priv->mask_source[i]; mask_source->prepare (mask_source, render, first); first = ART_FALSE; priv->callbacks[n_callbacks++] = &mask_source->super; } if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR)) priv->callbacks[n_callbacks++] = art_render_choose_clear_callback (render); priv->callbacks[n_callbacks++] = &image_source->super; /* Allocate image buffer and add compositing callback if needed. */ if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE)) { int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) * buf_depth) >> 3; render->buf_depth = buf_depth; render->buf_alpha = buf_alpha; render->image_buf = art_new (art_u8, width * bytespp); priv->callbacks[n_callbacks++] = art_render_choose_compositing_callback (render); } priv->n_callbacks = n_callbacks; if (render->need_span) render->span_x = art_new (int, width + 1); /* Invoke the driver */ if (best_driver >= 0) { ArtMaskSource *driver; driver = priv->mask_source[best_driver]; driver->invoke_driver (driver, render); } else { art_u8 *dest_ptr = render->pixels; int y; /* Dummy driver */ render->n_run = 2; render->run[0].x = render->x0; render->run[0].alpha = 0x8000 + 0xff * render->opacity; render->run[1].x = render->x1; render->run[1].alpha = 0x8000; if (render->need_span) { render->n_span = 2; render->span_x[0] = render->x0; render->span_x[1] = render->x1; } for (y = render->y0; y < render->y1; y++) { art_render_invoke_callbacks (render, dest_ptr, y); dest_ptr += render->rowstride; } } if (priv->mask_source != NULL) art_free (priv->mask_source); /* clean up callbacks */ for (i = 0; i < priv->n_callbacks; i++) { ArtRenderCallback *callback; callback = priv->callbacks[i]; callback->done (callback, render); } /* Tear down object */ if (render->alpha_buf != NULL) art_free (render->alpha_buf); if (render->image_buf != NULL) art_free (render->image_buf); art_free (render->run); if (render->span_x != NULL) art_free (render->span_x); art_free (priv->callbacks); art_free (render); } /** * art_render_mask_solid: Add a solid translucent mask. * @render: The render object. * @opacity: Opacity in [0..0x10000] form. * * Adds a translucent mask to the rendering object. **/ void art_render_mask_solid (ArtRender *render, int opacity) { art_u32 old_opacity = render->opacity; art_u32 new_opacity_tmp; if (opacity == 0x10000) /* avoid potential overflow */ return; new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000; render->opacity = new_opacity_tmp >> 16; } /** * art_render_add_mask_source: Add a mask source to the render object. * @render: Render object. * @mask_source: Mask source to add. * * This routine adds a mask source to the render object. In general, * client api's for adding mask sources should just take a render object, * then the mask source creation function should call this function. * Clients should never have to call this function directly, unless of * course they're creating custom mask sources. **/ void art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source) { ArtRenderPriv *priv = (ArtRenderPriv *)render; int n_mask_source = priv->n_mask_source++; if (n_mask_source == 0) priv->mask_source = art_new (ArtMaskSource *, 1); /* This predicate is true iff n_mask_source is a power of two */ else if (!(n_mask_source & (n_mask_source - 1))) priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *, n_mask_source << 1); priv->mask_source[n_mask_source] = mask_source; } /** * art_render_add_image_source: Add a mask source to the render object. * @render: Render object. * @image_source: Image source to add. * * This routine adds an image source to the render object. In general, * client api's for adding image sources should just take a render * object, then the mask source creation function should call this * function. Clients should never have to call this function * directly, unless of course they're creating custom image sources. **/ void art_render_add_image_source (ArtRender *render, ArtImageSource *image_source) { ArtRenderPriv *priv = (ArtRenderPriv *)render; if (priv->image_source != NULL) { art_warn ("art_render_add_image_source: image source already present.\n"); return; } priv->image_source = image_source; } /* Solid image source object and methods. Perhaps this should go into a separate file. */ typedef struct _ArtImageSourceSolid ArtImageSourceSolid; struct _ArtImageSourceSolid { ArtImageSource super; ArtPixMaxDepth color[ART_MAX_CHAN]; art_u32 *rgbtab; art_boolean init; }; static void art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render) { ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; if (z->rgbtab != NULL) art_free (z->rgbtab); art_free (self); } static void art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render) { ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; ArtPixMaxDepth color_max; int r_fg, g_fg, b_fg; int r_bg, g_bg, b_bg; int r, g, b; int dr, dg, db; int i; int tmp; art_u32 *rgbtab; rgbtab = art_new (art_u32, 256); z->rgbtab = rgbtab; color_max = self->color[0]; r_fg = ART_PIX_8_FROM_MAX (color_max); color_max = self->color[1]; g_fg = ART_PIX_8_FROM_MAX (color_max); color_max = self->color[2]; b_fg = ART_PIX_8_FROM_MAX (color_max); color_max = render->clear_color[0]; r_bg = ART_PIX_8_FROM_MAX (color_max); color_max = render->clear_color[1]; g_bg = ART_PIX_8_FROM_MAX (color_max); color_max = render->clear_color[2]; b_bg = ART_PIX_8_FROM_MAX (color_max); r = (r_bg << 16) + 0x8000; g = (g_bg << 16) + 0x8000; b = (b_bg << 16) + 0x8000; tmp = ((r_fg - r_bg) << 16) + 0x80; dr = (tmp + (tmp >> 8)) >> 8; tmp = ((g_fg - g_bg) << 16) + 0x80; dg = (tmp + (tmp >> 8)) >> 8; tmp = ((b_fg - b_bg) << 16) + 0x80; db = (tmp + (tmp >> 8)) >> 8; for (i = 0; i < 256; i++) { rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16); r += dr; g += dg; b += db; } } static void art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; ArtRenderMaskRun *run = render->run; int n_run = render->n_run; art_u32 *rgbtab = z->rgbtab; art_u32 rgb; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; int i; int ix; if (n_run > 0) { run_x1 = run[0].x; if (run_x1 > x0) { rgb = rgbtab[0]; art_rgb_fill_run (dest, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), run_x1 - x0); } for (i = 0; i < n_run - 1; i++) { run_x0 = run_x1; run_x1 = run[i + 1].x; rgb = rgbtab[(run[i].alpha >> 16) & 0xff]; ix = (run_x0 - x0) * 3; #define OPTIMIZE_LEN_1 #ifdef OPTIMIZE_LEN_1 if (run_x1 - run_x0 == 1) { dest[ix] = rgb >> 16; dest[ix + 1] = (rgb >> 8) & 0xff; dest[ix + 2] = rgb & 0xff; } else { art_rgb_fill_run (dest + ix, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), run_x1 - run_x0); } #else art_rgb_fill_run (dest + ix, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), run_x1 - run_x0); #endif } } else { run_x1 = x0; } if (run_x1 < x1) { rgb = rgbtab[0]; art_rgb_fill_run (dest + (run_x1 - x0) * 3, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), x1 - run_x1); } } static void art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; int width = render->x1 - render->x0; art_u8 r, g, b; ArtPixMaxDepth color_max; /* todo: replace this simple test with real sparseness */ if (z->init) return; z->init = ART_TRUE; color_max = z->color[0]; r = ART_PIX_8_FROM_MAX (color_max); color_max = z->color[1]; g = ART_PIX_8_FROM_MAX (color_max); color_max = z->color[2]; b = ART_PIX_8_FROM_MAX (color_max); art_rgb_fill_run (render->image_buf, r, g, b, width); } static void art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render, ArtImageSourceFlags *p_flags, int *p_buf_depth, ArtAlphaType *p_alpha) { ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; ArtImageSourceFlags flags = 0; static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y); render_cbk = NULL; if (render->depth == 8 && render->n_chan == 3 && render->alpha_type == ART_ALPHA_NONE) { if (render->clear) { render_cbk = art_render_image_solid_rgb8_opaq; flags |= ART_IMAGE_SOURCE_CAN_CLEAR | ART_IMAGE_SOURCE_CAN_COMPOSITE; art_render_image_solid_rgb8_opaq_init (z, render); } } if (render_cbk == NULL) { if (render->depth == 8) { render_cbk = art_render_image_solid_rgb8; *p_buf_depth = 8; *p_alpha = ART_ALPHA_NONE; /* todo */ } } /* todo: general case */ self->super.render = render_cbk; *p_flags = flags; } /** * art_render_image_solid: Add a solid color image source. * @render: The render object. * @color: Color. * * Adds an image source with the solid color given by @color. The * color need not be retained in memory after this call. **/ void art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color) { ArtImageSourceSolid *image_source; int i; image_source = art_new (ArtImageSourceSolid, 1); image_source->super.super.render = NULL; image_source->super.super.done = art_render_image_solid_done; image_source->super.negotiate = art_render_image_solid_negotiate; for (i = 0; i < render->n_chan; i++) image_source->color[i] = color[i]; image_source->rgbtab = NULL; image_source->init = ART_FALSE; art_render_add_image_source (render, &image_source->super); } rl-renderpm-4.0.3/src/libart_lgpl/art_render.h000066400000000000000000000111361453236046100213330ustar00rootroot00000000000000/* * art_render.h: Modular rendering architecture. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RENDER_H__ #define __ART_RENDER_H__ #ifdef LIBART_COMPILATION #include "art_alphagamma.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Render object */ #ifndef ART_MAX_DEPTH #define ART_MAX_DEPTH 16 #endif #if ART_MAX_DEPTH == 16 typedef art_u16 ArtPixMaxDepth; #define ART_PIX_MAX_FROM_8(x) ((x) | ((x) << 8)) #define ART_PIX_8_FROM_MAX(x) (((x) + 0x80 - (((x) + 0x80) >> 8)) >> 8) #else #if ART_MAX_DEPTH == 8 typedef art_u8 ArtPixMaxDepth; #define ART_PIX_MAX_FROM_8(x) (x) #define ART_PIX_8_FROM_MAX(x) (x) #else #error ART_MAX_DEPTH must be either 8 or 16 #endif #endif #define ART_MAX_CHAN 16 typedef struct _ArtRender ArtRender; typedef struct _ArtRenderCallback ArtRenderCallback; typedef struct _ArtRenderMaskRun ArtRenderMaskRun; typedef struct _ArtImageSource ArtImageSource; typedef struct _ArtMaskSource ArtMaskSource; typedef enum { ART_ALPHA_NONE = 0, ART_ALPHA_SEPARATE = 1, ART_ALPHA_PREMUL = 2 } ArtAlphaType; typedef enum { ART_COMPOSITE_NORMAL, ART_COMPOSITE_MULTIPLY, /* todo: more */ ART_COMPOSITE_CUSTOM } ArtCompositingMode; typedef enum { ART_IMAGE_SOURCE_CAN_CLEAR = 1, ART_IMAGE_SOURCE_CAN_COMPOSITE = 2 } ArtImageSourceFlags; struct _ArtRenderMaskRun { int x; int alpha; }; struct _ArtRenderCallback { void (*render) (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y); void (*done) (ArtRenderCallback *self, ArtRender *render); }; struct _ArtImageSource { ArtRenderCallback super; void (*negotiate) (ArtImageSource *self, ArtRender *render, ArtImageSourceFlags *p_flags, int *p_buf_depth, ArtAlphaType *p_alpha_type); }; struct _ArtMaskSource { ArtRenderCallback super; int (*can_drive) (ArtMaskSource *self, ArtRender *render); /* For each mask source, ::prepare() is invoked if it is not a driver, or ::invoke_driver() if it is. */ void (*invoke_driver) (ArtMaskSource *self, ArtRender *render); void (*prepare) (ArtMaskSource *self, ArtRender *render, art_boolean first); }; struct _ArtRender { /* parameters of destination image */ int x0, y0; int x1, y1; art_u8 *pixels; int rowstride; int n_chan; int depth; ArtAlphaType alpha_type; art_boolean clear; ArtPixMaxDepth clear_color[ART_MAX_CHAN + 1]; art_u32 opacity; /* [0..0x10000] */ ArtCompositingMode compositing_mode; ArtAlphaGamma *alphagamma; art_u8 *alpha_buf; /* parameters of intermediate buffer */ int buf_depth; ArtAlphaType buf_alpha; art_u8 *image_buf; /* driving alpha scanline data */ /* A "run" is a contiguous sequence of x values with the same alpha value. */ int n_run; ArtRenderMaskRun *run; /* A "span" is a contiguous sequence of x values with non-zero alpha. */ int n_span; int *span_x; art_boolean need_span; }; ArtRender * art_render_new (int x0, int y0, int x1, int y1, art_u8 *pixels, int rowstride, int n_chan, int depth, ArtAlphaType alpha_type, ArtAlphaGamma *alphagamma); void art_render_invoke (ArtRender *render); void art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color); void art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb); void art_render_mask_solid (ArtRender *render, int opacity); void art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color); /* The next two functions are for custom mask sources only. */ void art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source); void art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y); /* The following function is for custom image sources only. */ void art_render_add_image_source (ArtRender *render, ArtImageSource *image_source); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_RENDER_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_render_gradient.c000066400000000000000000000434011453236046100232030ustar00rootroot00000000000000/* * art_render_gradient.c: Gradient image source for modular rendering. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Raph Levien * Alexander Larsson */ #include "config.h" #include "art_render_gradient.h" #include #include #include #include #include /* Hack to find out how to define alloca on different platforms. * Modified version of glib/galloca.h. */ #ifdef __GNUC__ /* GCC does the right thing */ # undef alloca # define alloca(size) __builtin_alloca (size) #elif defined (HAVE_ALLOCA_H) /* a native and working alloca.h is there */ # include #else /* !__GNUC__ && !HAVE_ALLOCA_H */ # ifdef _MSC_VER # include # define alloca _alloca # else /* !_MSC_VER */ # ifdef _AIX #pragma alloca # else /* !_AIX */ # ifndef alloca /* predefined by HP cc +Olibcalls */ char *alloca (); # endif /* !alloca */ # endif /* !_AIX */ # endif /* !_MSC_VER */ #endif /* !__GNUC__ && !HAVE_ALLOCA_H */ #undef DEBUG_SPEW typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin; typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad; struct _ArtImageSourceGradLin { ArtImageSource super; const ArtGradientLinear *gradient; }; struct _ArtImageSourceGradRad { ArtImageSource super; const ArtGradientRadial *gradient; double a; }; #define EPSILON 1e-6 #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /* MAX */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif /* MIN */ static void art_rgba_gradient_run (art_u8 *buf, art_u8 *color1, art_u8 *color2, int len) { int i; int r, g, b, a; int dr, dg, db, da; #ifdef DEBUG_SPEW printf ("gradient run from %3d %3d %3d %3d to %3d %3d %3d %3d in %d pixels\n", color1[0], color1[1], color1[2], color1[3], color2[0], color2[1], color2[2], color2[3], len); #endif r = (color1[0] << 16) + 0x8000; g = (color1[1] << 16) + 0x8000; b = (color1[2] << 16) + 0x8000; a = (color1[3] << 16) + 0x8000; dr = ((color2[0] - color1[0]) << 16) / len; dg = ((color2[1] - color1[1]) << 16) / len; db = ((color2[2] - color1[2]) << 16) / len; da = ((color2[3] - color1[3]) << 16) / len; for (i = 0; i < len; i++) { *buf++ = (r>>16); *buf++ = (g>>16); *buf++ = (b>>16); *buf++ = (a>>16); r += dr; g += dg; b += db; a += da; } } static void calc_color_at (ArtGradientStop *stops, int n_stops, ArtGradientSpread spread, double offset, double offset_fraction, int favor_start, int ix, art_u8 *color) { double off0, off1; int j; if (spread == ART_GRADIENT_PAD) { if (offset < 0.0) { color[0] = ART_PIX_8_FROM_MAX (stops[0].color[0]); color[1] = ART_PIX_8_FROM_MAX (stops[0].color[1]); color[2] = ART_PIX_8_FROM_MAX (stops[0].color[2]); color[3] = ART_PIX_8_FROM_MAX (stops[0].color[3]); return; } if (offset >= 1.0) { color[0] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[0]); color[1] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[1]); color[2] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[2]); color[3] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[3]); return; } } if (ix > 0 && ix < n_stops) { off0 = stops[ix - 1].offset; off1 = stops[ix].offset; if (fabs (off1 - off0) > EPSILON) { double interp; double o; o = offset_fraction; if ((fabs (o) < EPSILON) && (!favor_start)) o = 1.0; else if ((fabs (o-1.0) < EPSILON) && (favor_start)) o = 0.0; /* if (offset_fraction == 0.0 && !favor_start) offset_fraction = 1.0; */ interp = (o - off0) / (off1 - off0); for (j = 0; j < 4; j++) { int z0, z1; int z; z0 = stops[ix - 1].color[j]; z1 = stops[ix].color[j]; z = (int)floor (z0 + (z1 - z0) * interp + 0.5); color[j] = ART_PIX_8_FROM_MAX (z); } return; } /* If offsets are too close to safely do the division, just pick the ix color. */ color[0] = ART_PIX_8_FROM_MAX (stops[ix].color[0]); color[1] = ART_PIX_8_FROM_MAX (stops[ix].color[1]); color[2] = ART_PIX_8_FROM_MAX (stops[ix].color[2]); color[3] = ART_PIX_8_FROM_MAX (stops[ix].color[3]); return; } printf ("WARNING! bad ix %d in calc_color_at() [internal error]\n", ix); assert (0); } static void art_render_gradient_linear_render_8 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self; const ArtGradientLinear *gradient = z->gradient; int i; int width = render->x1 - render->x0; int len; double offset, d_offset; double offset_fraction; int next_stop; int ix; art_u8 color1[4], color2[4]; int n_stops = gradient->n_stops; int extra_stops; ArtGradientStop *stops = gradient->stops; ArtGradientStop *tmp_stops; art_u8 *bufp = render->image_buf; ArtGradientSpread spread = gradient->spread; #ifdef DEBUG_SPEW printf ("x1: %d, x2: %d, y: %d\n", render->x0, render->x1, y); printf ("spread: %d, stops:", gradient->spread); for (i=0;istops[i].offset); } printf ("\n"); #endif offset = render->x0 * gradient->a + y * gradient->b + gradient->c; d_offset = gradient->a; /* We need to force the gradient to extend the whole 0..1 segment, because the rest of the code doesn't handle partial gradients correctly */ if ((gradient->stops[0].offset != 0.0) || (gradient->stops[n_stops-1].offset != 1.0)) { extra_stops = 0; tmp_stops = stops = (void *)alloca (sizeof (ArtGradientStop) * (n_stops + 2)); if (gradient->stops[0].offset != 0.0) { memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop)); tmp_stops[0].offset = 0.0; tmp_stops += 1; extra_stops++; } memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop) * n_stops); if (gradient->stops[n_stops-1].offset != 1.0) { tmp_stops += n_stops; memcpy (tmp_stops, &gradient->stops[n_stops-1], sizeof (ArtGradientStop)); tmp_stops[0].offset = 1.0; extra_stops++; } n_stops += extra_stops; #ifdef DEBUG_SPEW printf ("start/stop modified stops:"); for (i=0;i offset_fraction || (d_offset < 0.0 && stops[ix].offset == offset_fraction)) break; if (ix == 0) ix = n_stops - 1; #ifdef DEBUG_SPEW printf ("Initial ix: %d\n", ix); #endif assert (ix > 0); assert (ix < n_stops); assert ((stops[ix-1].offset <= offset_fraction) || ((stops[ix].offset == 1.0) && (offset_fraction == 0.0))); assert (offset_fraction <= stops[ix].offset); assert ((offset_fraction != stops[ix-1].offset) || (d_offset >= 0.0)); assert ((offset_fraction != stops[ix].offset) || (d_offset <= 0.0)); while (width > 0) { #ifdef DEBUG_SPEW printf ("ix: %d\n", ix); printf ("start offset: %f\n", offset); #endif calc_color_at (stops, n_stops, spread, offset, offset_fraction, (d_offset > 0.0), ix, color1); if (d_offset > 0) next_stop = ix; else next_stop = ix-1; #ifdef DEBUG_SPEW printf ("next_stop: %d\n", next_stop); #endif if (fabs (d_offset) > EPSILON) { double o; o = offset_fraction; if ((fabs (o) <= EPSILON) && (ix == n_stops - 1)) o = 1.0; else if ((fabs (o-1.0) <= EPSILON) && (ix == 1)) o = 0.0; #ifdef DEBUG_SPEW printf ("o: %f\n", o); #endif len = (int)floor (fabs ((stops[next_stop].offset - o) / d_offset)) + 1; len = MAX (len, 0); len = MIN (len, width); } else { len = width; } #ifdef DEBUG_SPEW printf ("len: %d\n", len); #endif if (len > 0) { offset = offset + (len-1) * d_offset; offset_fraction = offset - floor (offset); #ifdef DEBUG_SPEW printf ("end offset: %f, fraction: %f\n", offset, offset_fraction); #endif calc_color_at (stops, n_stops, spread, offset, offset_fraction, (d_offset < 0.0), ix, color2); art_rgba_gradient_run (bufp, color1, color2, len); offset += d_offset; offset_fraction = offset - floor (offset); } if (d_offset > 0) { do { ix++; if (ix == n_stops) ix = 1; /* Note: offset_fraction can actually be one here on x86 machines that does calculations with extanded precision, but later rounds to 64bit. This happens if the 80bit offset_fraction is larger than the largest 64bit double that is less than one. */ } while (!((stops[ix-1].offset <= offset_fraction && offset_fraction < stops[ix].offset) || (ix == 1 && offset_fraction == 1.0))); } else { do { ix--; if (ix == 0) ix = n_stops - 1; } while (!((stops[ix-1].offset < offset_fraction && offset_fraction <= stops[ix].offset) || (ix == n_stops - 1 && offset_fraction == 0.0))); } bufp += 4*len; width -= len; } } /** * art_render_gradient_setpix: Set a gradient pixel. * @render: The render object. * @dst: Pointer to destination (where to store pixel). * @n_stops: Number of stops in @stops. * @stops: The stops for the gradient. * @offset: The offset. * * @n_stops must be > 0. * * Sets a gradient pixel, storing it at @dst. **/ static void art_render_gradient_setpix (ArtRender *render, art_u8 *dst, int n_stops, ArtGradientStop *stops, double offset) { int ix; int j; double off0, off1; int n_ch = render->n_chan + 1; for (ix = 0; ix < n_stops; ix++) if (stops[ix].offset > offset) break; /* stops[ix - 1].offset < offset < stops[ix].offset */ if (ix > 0 && ix < n_stops) { off0 = stops[ix - 1].offset; off1 = stops[ix].offset; if (fabs (off1 - off0) > EPSILON) { double interp; interp = (offset - off0) / (off1 - off0); for (j = 0; j < n_ch; j++) { int z0, z1; int z; z0 = stops[ix - 1].color[j]; z1 = stops[ix].color[j]; z = (int)floor (z0 + (z1 - z0) * interp + 0.5); if (render->buf_depth == 8) dst[j] = ART_PIX_8_FROM_MAX (z); else /* (render->buf_depth == 16) */ ((art_u16 *)dst)[j] = z; } return; } } else if (ix == n_stops) ix--; for (j = 0; j < n_ch; j++) { int z; z = stops[ix].color[j]; if (render->buf_depth == 8) dst[j] = ART_PIX_8_FROM_MAX (z); else /* (render->buf_depth == 16) */ ((art_u16 *)dst)[j] = z; } } static void art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render) { art_free (self); } static void art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self; const ArtGradientLinear *gradient = z->gradient; int pixstride = (render->n_chan + 1) * (render->depth >> 3); int x; int width = render->x1 - render->x0; double offset, d_offset; double actual_offset; int n_stops = gradient->n_stops; ArtGradientStop *stops = gradient->stops; art_u8 *bufp = render->image_buf; ArtGradientSpread spread = gradient->spread; offset = render->x0 * gradient->a + y * gradient->b + gradient->c; d_offset = gradient->a; for (x = 0; x < width; x++) { if (spread == ART_GRADIENT_PAD) actual_offset = offset; else if (spread == ART_GRADIENT_REPEAT) actual_offset = offset - floor (offset); else /* (spread == ART_GRADIENT_REFLECT) */ { double tmp; tmp = offset - 2 * floor (0.5 * offset); actual_offset = tmp > 1 ? 2 - tmp : tmp; } art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset); offset += d_offset; bufp += pixstride; } } static void art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render, ArtImageSourceFlags *p_flags, int *p_buf_depth, ArtAlphaType *p_alpha) { if (render->depth == 8 && render->n_chan == 3) { self->super.render = art_render_gradient_linear_render_8; *p_flags = 0; *p_buf_depth = 8; *p_alpha = ART_ALPHA_PREMUL; return; } self->super.render = art_render_gradient_linear_render; *p_flags = 0; *p_buf_depth = render->depth; *p_alpha = ART_ALPHA_PREMUL; } /** * art_render_gradient_linear: Add a linear gradient image source. * @render: The render object. * @gradient: The linear gradient. * * Adds the linear gradient @gradient as the image source for rendering * in the render object @render. **/ void art_render_gradient_linear (ArtRender *render, const ArtGradientLinear *gradient, ArtFilterLevel level) { ArtImageSourceGradLin *image_source = art_new (ArtImageSourceGradLin, 1); image_source->super.super.render = NULL; image_source->super.super.done = art_render_gradient_linear_done; image_source->super.negotiate = art_render_gradient_linear_negotiate; image_source->gradient = gradient; art_render_add_image_source (render, &image_source->super); } static void art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render) { art_free (self); } static void art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self; const ArtGradientRadial *gradient = z->gradient; int pixstride = (render->n_chan + 1) * (render->depth >> 3); int x; int x0 = render->x0; int width = render->x1 - x0; int n_stops = gradient->n_stops; ArtGradientStop *stops = gradient->stops; art_u8 *bufp = render->image_buf; double fx = gradient->fx; double fy = gradient->fy; double dx, dy; const double *affine = gradient->affine; double aff0 = affine[0]; double aff1 = affine[1]; const double a = z->a; const double arecip = 1.0 / a; double b, db; double c, dc, ddc; double b_a, db_a; double rad, drad, ddrad; dx = x0 * aff0 + y * affine[2] + affine[4] - fx; dy = x0 * aff1 + y * affine[3] + affine[5] - fy; b = dx * fx + dy * fy; db = aff0 * fx + aff1 * fy; c = dx * dx + dy * dy; dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1; ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1; b_a = b * arecip; db_a = db * arecip; rad = b_a * b_a + c * arecip; drad = 2 * b_a * db_a + db_a * db_a + dc * arecip; ddrad = 2 * db_a * db_a + ddc * arecip; for (x = 0; x < width; x++) { double z; if (rad > 0) z = b_a + sqrt (rad); else z = b_a; art_render_gradient_setpix (render, bufp, n_stops, stops, z); bufp += pixstride; b_a += db_a; rad += drad; drad += ddrad; } } static void art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render, ArtImageSourceFlags *p_flags, int *p_buf_depth, ArtAlphaType *p_alpha) { self->super.render = art_render_gradient_radial_render; *p_flags = 0; *p_buf_depth = render->depth; *p_alpha = ART_ALPHA_PREMUL; } /** * art_render_gradient_radial: Add a radial gradient image source. * @render: The render object. * @gradient: The radial gradient. * * Adds the radial gradient @gradient as the image source for rendering * in the render object @render. **/ void art_render_gradient_radial (ArtRender *render, const ArtGradientRadial *gradient, ArtFilterLevel level) { ArtImageSourceGradRad *image_source = art_new (ArtImageSourceGradRad, 1); double fx = gradient->fx; double fy = gradient->fy; image_source->super.super.render = NULL; image_source->super.super.done = art_render_gradient_radial_done; image_source->super.negotiate = art_render_gradient_radial_negotiate; image_source->gradient = gradient; /* todo: sanitycheck fx, fy? */ image_source->a = 1 - fx * fx - fy * fy; art_render_add_image_source (render, &image_source->super); } rl-renderpm-4.0.3/src/libart_lgpl/art_render_gradient.h000066400000000000000000000044271453236046100232150ustar00rootroot00000000000000/* * art_render_gradient.h: Gradient image source for modular rendering. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Raph Levien * Alexander Larsson */ #ifndef __ART_RENDER_GRADIENT_H__ #define __ART_RENDER_GRADIENT_H__ #ifdef LIBART_COMPILATION #include "art_filterlevel.h" #include "art_render.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtGradientLinear ArtGradientLinear; typedef struct _ArtGradientRadial ArtGradientRadial; typedef struct _ArtGradientStop ArtGradientStop; typedef enum { ART_GRADIENT_PAD, ART_GRADIENT_REFLECT, ART_GRADIENT_REPEAT } ArtGradientSpread; struct _ArtGradientLinear { double a; double b; double c; ArtGradientSpread spread; int n_stops; ArtGradientStop *stops; }; struct _ArtGradientRadial { double affine[6]; /* transforms user coordinates to unit circle */ double fx, fy; /* focal point in unit circle coords */ int n_stops; ArtGradientStop *stops; }; struct _ArtGradientStop { double offset; ArtPixMaxDepth color[ART_MAX_CHAN + 1]; }; void art_render_gradient_linear (ArtRender *render, const ArtGradientLinear *gradient, ArtFilterLevel level); void art_render_gradient_radial (ArtRender *render, const ArtGradientRadial *gradient, ArtFilterLevel level); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_RENDER_GRADIENT_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_render_mask.c000066400000000000000000000101361453236046100223400ustar00rootroot00000000000000/* * art_render_mask.c: Alpha mask source for modular rendering. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Raph Levien */ #include "config.h" #include "art_render_mask.h" #include typedef struct _ArtMaskSourceMask ArtMaskSourceMask; struct _ArtMaskSourceMask { ArtMaskSource super; ArtRender *render; art_boolean first; int x0; int y0; int x1; int y1; const art_u8 *mask_buf; int rowstride; }; static void art_render_mask_done (ArtRenderCallback *self, ArtRender *render) { art_free (self); } static int art_render_mask_can_drive (ArtMaskSource *self, ArtRender *render) { return 0; } static void art_render_mask_render (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, int y) { ArtMaskSourceMask *z = (ArtMaskSourceMask *)self; int x0 = render->x0, x1 = render->x1; int z_x0 = z->x0, z_x1 = z->x1; int width = x1 - x0; int z_width = z_x1 - z_x0; art_u8 *alpha_buf = render->alpha_buf; if (y < z->y0 || y >= z->y1 || z_width <= 0) memset (alpha_buf, 0, width); else { const art_u8 *src_line = z->mask_buf + (y - z->y0) * z->rowstride; art_u8 *dst_line = alpha_buf + z_x0 - x0; if (z_x0 > x0) memset (alpha_buf, 0, z_x0 - x0); if (z->first) memcpy (dst_line, src_line, z_width); else { int x; for (x = 0; x < z_width; x++) { int v; v = src_line[x]; if (v) { v = v * dst_line[x] + 0x80; v = (v + (v >> 8)) >> 8; dst_line[x] = v; } else { dst_line[x] = 0; } } } if (z_x1 < x1) memset (alpha_buf + z_x1 - x0, 0, x1 - z_x1); } } static void art_render_mask_prepare (ArtMaskSource *self, ArtRender *render, art_boolean first) { ArtMaskSourceMask *z = (ArtMaskSourceMask *)self; self->super.render = art_render_mask_render; z->first = first; } /** * art_render_mask: Use an alpha buffer as a render mask source. * @render: Render object. * @x0: Left coordinate of mask rect. * @y0: Top coordinate of mask rect. * @x1: Right coordinate of mask rect. * @y1: Bottom coordinate of mask rect. * @mask_buf: Buffer containing 8bpp alpha mask data. * @rowstride: Rowstride of @mask_buf. * * Adds @mask_buf to the render object as a mask. Note: @mask_buf must * remain allocated until art_render_invoke() is called on @render. **/ void art_render_mask (ArtRender *render, int x0, int y0, int x1, int y1, const art_u8 *mask_buf, int rowstride) { ArtMaskSourceMask *mask_source; if (x0 < render->x0) { mask_buf += render->x0 - x0; x0 = render->x0; } if (x1 > render->x1) x1 = render->x1; if (y0 < render->y0) { mask_buf += (render->y0 - y0) * rowstride; y0 = render->y0; } if (y1 > render->y1) y1 = render->y1; mask_source = art_new (ArtMaskSourceMask, 1); mask_source->super.super.render = NULL; mask_source->super.super.done = art_render_mask_done; mask_source->super.can_drive = art_render_mask_can_drive; mask_source->super.invoke_driver = NULL; mask_source->super.prepare = art_render_mask_prepare; mask_source->render = render; mask_source->x0 = x0; mask_source->y0 = y0; mask_source->x1 = x1; mask_source->y1 = y1; mask_source->mask_buf = mask_buf; mask_source->rowstride = rowstride; art_render_add_mask_source (render, &mask_source->super); } rl-renderpm-4.0.3/src/libart_lgpl/art_render_mask.h000066400000000000000000000025301453236046100223440ustar00rootroot00000000000000/* * art_render_mask.h: Alpha mask source for modular rendering. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Raph Levien */ #ifndef __ART_RENDER_MASK_H__ #define __ART_RENDER_MASK_H__ #ifdef LIBART_COMPILATION #include "art_render.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void art_render_mask (ArtRender *render, int x0, int y0, int x1, int y1, const art_u8 *mask_buf, int rowstride); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_RENDER_MASK_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_render_svp.c000066400000000000000000000234541453236046100222240ustar00rootroot00000000000000/* * art_render_gradient.c: SVP mask source for modular rendering. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Raph Levien */ #include "art_render_svp.h" #include "art_svp_render_aa.h" typedef struct _ArtMaskSourceSVP ArtMaskSourceSVP; struct _ArtMaskSourceSVP { ArtMaskSource super; ArtRender *render; const ArtSVP *svp; art_u8 *dest_ptr; }; static void art_render_svp_done (ArtRenderCallback *self, ArtRender *render) { art_free (self); } static int art_render_svp_can_drive (ArtMaskSource *self, ArtRender *render) { return 10; } /* The basic art_render_svp_callback function is repeated four times, for all combinations of non-unit opacity and generating spans. In general, I'd consider this bad style, but in this case I plead a measurable performance improvement. */ static void art_render_svp_callback (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int i; int running_sum = start; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0 && running_sum > 0x80ff) { run[0].x = x0; run[0].alpha = running_sum; n_run++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; run[n_run].alpha = running_sum; n_run++; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; run[n_run].alpha = running_sum; n_run++; } if (running_sum > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; } render->n_run = n_run; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_callback_span (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int n_span = 0; int i; int running_sum = start; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; int *span_x = render->span_x; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0 && running_sum > 0x80ff) { run[0].x = x0; run[0].alpha = running_sum; n_run++; span_x[0] = x0; n_span++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; run[n_run].alpha = running_sum; n_run++; if ((n_span & 1) != (running_sum > 0x80ff)) span_x[n_span++] = run_x0; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; run[n_run].alpha = running_sum; n_run++; if ((n_span & 1) != (running_sum > 0x80ff)) span_x[n_span++] = run_x1; } if (running_sum > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; span_x[n_span++] = x1; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; span_x[0] = x0; span_x[1] = x1; n_span = 2; } render->n_run = n_run; render->n_span = n_span; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_callback_opacity (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int i; art_u32 running_sum; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; art_u32 opacity = render->opacity; art_u32 alpha; running_sum = start - 0x7f80; if (n_steps > 0) { run_x1 = steps[0].x; alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8; if (run_x1 > x0 && alpha > 0x80ff) { run[0].x = x0; run[0].alpha = alpha; n_run++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8; run[n_run].alpha = alpha; n_run++; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8; run[n_run].alpha = alpha; n_run++; } if (alpha > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; } render->n_run = n_run; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_callback_opacity_span (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int n_span = 0; int i; art_u32 running_sum; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; int *span_x = render->span_x; art_u32 opacity = render->opacity; art_u32 alpha; running_sum = start - 0x7f80; if (n_steps > 0) { run_x1 = steps[0].x; alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8; if (run_x1 > x0 && alpha > 0x80ff) { run[0].x = x0; run[0].alpha = alpha; n_run++; span_x[0] = x0; n_span++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8; run[n_run].alpha = alpha; n_run++; if ((n_span & 1) != (alpha > 0x80ff)) span_x[n_span++] = run_x0; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8; run[n_run].alpha = alpha; n_run++; if ((n_span & 1) != (alpha > 0x80ff)) span_x[n_span++] = run_x1; } if (alpha > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; span_x[n_span++] = x1; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; span_x[0] = x0; span_x[1] = x1; n_span = 2; } render->n_run = n_run; render->n_span = n_span; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_invoke_driver (ArtMaskSource *self, ArtRender *render) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)self; void (*callback) (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps); z->dest_ptr = render->pixels; if (render->opacity == 0x10000) { if (render->need_span) callback = art_render_svp_callback_span; else callback = art_render_svp_callback; } else { if (render->need_span) callback = art_render_svp_callback_opacity_span; else callback = art_render_svp_callback_opacity; } art_svp_render_aa (z->svp, render->x0, render->y0, render->x1, render->y1, callback, self); art_render_svp_done (&self->super, render); } static void art_render_svp_prepare (ArtMaskSource *self, ArtRender *render, art_boolean first) { /* todo */ art_die ("art_render_svp non-driver mode not yet implemented.\n"); } /** * art_render_svp: Use an SVP as a render mask source. * @render: Render object. * @svp: SVP. * * Adds @svp to the render object as a mask. Note: @svp must remain * allocated until art_render_invoke() is called on @render. **/ void art_render_svp (ArtRender *render, const ArtSVP *svp) { ArtMaskSourceSVP *mask_source; mask_source = art_new (ArtMaskSourceSVP, 1); mask_source->super.super.render = NULL; mask_source->super.super.done = art_render_svp_done; mask_source->super.can_drive = art_render_svp_can_drive; mask_source->super.invoke_driver = art_render_svp_invoke_driver; mask_source->super.prepare = art_render_svp_prepare; mask_source->render = render; mask_source->svp = svp; art_render_add_mask_source (render, &mask_source->super); } rl-renderpm-4.0.3/src/libart_lgpl/art_render_svp.h000066400000000000000000000025151453236046100222240ustar00rootroot00000000000000/* * art_render_svp.h: SVP mask source for modular rendering. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Raph Levien */ #ifndef __ART_RENDER_SVP_H__ #define __ART_RENDER_SVP_H__ #ifdef LIBART_COMPILATION #include "art_render.h" #include "art_svp.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void art_render_svp (ArtRender *render, const ArtSVP *svp); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_RENDER_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_rgb.c000066400000000000000000000105041453236046100206170ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgb.h" #include /* for memset */ /* Basic operators for manipulating 24-bit packed RGB buffers. */ #define COLOR_RUN_COMPLEX #ifdef COLOR_RUN_SIMPLE /* This is really slow. Is there any way we might speed it up? Two ideas: First, maybe we should be working at 32-bit alignment. Then, this can be a simple loop over word stores. Second, we can keep working at 24-bit alignment, but have some intelligence about storing. For example, we can iterate over 4-pixel chunks (aligned at 4 pixels), with an inner loop something like: *buf++ = v1; *buf++ = v2; *buf++ = v3; One source of extra complexity is the need to make sure linebuf is aligned to a 32-bit boundary. This second alternative has some complexity to it, but is appealing because it really minimizes the memory bandwidth. */ void art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, gint n) { int i; if (r == g && g == b) { memset (buf, g, n + n + n); } else { for (i = 0; i < n; i++) { *buf++ = r; *buf++ = g; *buf++ = b; } } } #endif #ifdef COLOR_RUN_COMPLEX /* This implements the second of the two ideas above. The test results are _very_ encouraging - it seems the speed is within 10% of memset, which is quite good! */ /** * art_rgb_fill_run: fill a buffer a solid RGB color. * @buf: Buffer to fill. * @r: Red, range 0..255. * @g: Green, range 0..255. * @b: Blue, range 0..255. * @n: Number of RGB triples to fill. * * Fills a buffer with @n copies of the (@r, @g, @b) triple. Thus, * locations @buf (inclusive) through @buf + 3 * @n (exclusive) are * written. * * The implementation of this routine is very highly optimized. **/ void art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n) { int i; unsigned int v1, v2, v3; if (r == g && g == b) { memset (buf, g, n + n + n); } else { if (n < 8) { for (i = 0; i < n; i++) { *buf++ = r; *buf++ = g; *buf++ = b; } } else { /* handle prefix up to byte alignment */ /* I'm worried about this cast on sizeof(long) != sizeof(uchar *) architectures, but it _should_ work. */ for (i = 0; ((unsigned long)buf) & 3; i++) { *buf++ = r; *buf++ = g; *buf++ = b; } #ifndef WORDS_BIGENDIAN v1 = r | (g << 8) | (b << 16) | (r << 24); v3 = (v1 << 8) | b; v2 = (v3 << 8) | g; #else v1 = (r << 24) | (g << 16) | (b << 8) | r; v2 = (v1 << 8) | g; v3 = (v2 << 8) | b; #endif for (; i < n - 3; i += 4) { ((art_u32 *)buf)[0] = v1; ((art_u32 *)buf)[1] = v2; ((art_u32 *)buf)[2] = v3; buf += 12; } /* handle postfix */ for (; i < n; i++) { *buf++ = r; *buf++ = g; *buf++ = b; } } } } #endif /** * art_rgb_run_alpha: Render semitransparent color over RGB buffer. * @buf: Buffer for rendering. * @r: Red, range 0..255. * @g: Green, range 0..255. * @b: Blue, range 0..255. * @alpha: Alpha, range 0..256. * @n: Number of RGB triples to render. * * Renders a sequential run of solid (@r, @g, @b) color over @buf with * opacity @alpha. **/ void art_rgb_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n) { int i; int v; for (i = 0; i < n; i++) { v = *buf; *buf++ = v + (((r - v) * alpha + 0x80) >> 8); v = *buf; *buf++ = v + (((g - v) * alpha + 0x80) >> 8); v = *buf; *buf++ = v + (((b - v) * alpha + 0x80) >> 8); } } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb.h000066400000000000000000000023041453236046100206230ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_H__ #define __ART_RGB_H__ #ifdef LIBART_COMPILATION #include "art_misc.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif void art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n); void art_rgb_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_a_affine.c000066400000000000000000000107041453236046100224310ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgb_a_affine.h" #include #include "art_affine.h" #include "art_point.h" #include "art_rgb_affine_private.h" /* This module handles compositing of affine-transformed alpha only images over rgb pixel buffers. */ /* Composite the source image over the destination image, applying the affine transform. */ /** * art_rgb_a_affine: Affine transform source Alpha image and composite. * @dst: Destination image RGB buffer. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @dst_rowstride: Rowstride of @dst buffer. * @src: Source image alpha buffer. * @src_width: Width of source image. * @src_height: Height of source image. * @src_rowstride: Rowstride of @src buffer. * @rgb: RGB foreground color, in 0xRRGGBB. * @affine: Affine transform. * @level: Filter level. * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing. * * Affine transform the solid color rgb with alpha specified by the * source image stored in @src, compositing over the area of destination * image @dst specified by the rectangle (@x0, @y0) - (@x1, @y1). * As usual in libart, the left and top edges of this rectangle are * included, and the right and bottom edges are excluded. * * The @alphagamma parameter specifies that the alpha compositing be * done in a gamma-corrected color space. In the current * implementation, it is ignored. * * The @level parameter specifies the speed/quality tradeoff of the * image interpolation. Currently, only ART_FILTER_NEAREST is * implemented. **/ void art_rgb_a_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, art_u32 rgb, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma) { /* Note: this is a slow implementation, and is missing all filter levels other than NEAREST. It is here for clarity of presentation and to establish the interface. */ int x, y; double inv[6]; art_u8 *dst_p, *dst_linestart; const art_u8 *src_p; ArtPoint pt, src_pt; int src_x, src_y; int alpha; art_u8 bg_r, bg_g, bg_b; art_u8 fg_r, fg_g, fg_b; int tmp; int run_x0, run_x1; art_u8 r, g, b; r = (rgb>>16)&0xff; g = (rgb>>8)&0xff; b = (rgb)&0xff; dst_linestart = dst; art_affine_invert (inv, affine); for (y = y0; y < y1; y++) { pt.y = y + 0.5; run_x0 = x0; run_x1 = x1; art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height, inv); dst_p = dst_linestart + (run_x0 - x0) * 3; for (x = run_x0; x < run_x1; x++) { pt.x = x + 0.5; art_affine_point (&src_pt, &pt, inv); src_x = (int)floor (src_pt.x); src_y = (int)floor (src_pt.y); src_p = src + (src_y * src_rowstride) + src_x; if (src_x >= 0 && src_x < src_width && src_y >= 0 && src_y < src_height) { alpha = *src_p; if (alpha) { if (alpha == 255) { dst_p[0] = r; dst_p[1] = g; dst_p[2] = b; } else { bg_r = dst_p[0]; bg_g = dst_p[1]; bg_b = dst_p[2]; tmp = (r - bg_r) * alpha; fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8); tmp = (g - bg_g) * alpha; fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8); tmp = (b - bg_b) * alpha; fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8); dst_p[0] = fg_r; dst_p[1] = fg_g; dst_p[2] = fg_b; } } } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; } dst_p += 3; } dst_linestart += dst_rowstride; } } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_a_affine.h000066400000000000000000000027731453236046100224450ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_A_AFFINE_H__ #define __ART_RGB_A_AFFINE_H__ /* This module handles compositing of affine-transformed alpha only images over rgb pixel buffers. */ #ifdef LIBART_COMPILATION #include "art_filterlevel.h" #include "art_alphagamma.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif void art_rgb_a_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, art_u32 rgb, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_affine.c000066400000000000000000000070771453236046100221420ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgb_affine.h" #include #include "art_misc.h" #include "art_point.h" #include "art_affine.h" #include "art_rgb_affine_private.h" /* This module handles compositing of affine-transformed rgb images over rgb pixel buffers. */ /** * art_rgb_affine: Affine transform source RGB image and composite. * @dst: Destination image RGB buffer. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @dst_rowstride: Rowstride of @dst buffer. * @src: Source image RGB buffer. * @src_width: Width of source image. * @src_height: Height of source image. * @src_rowstride: Rowstride of @src buffer. * @affine: Affine transform. * @level: Filter level. * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing. * * Affine transform the source image stored in @src, compositing over * the area of destination image @dst specified by the rectangle * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges * of this rectangle are included, and the right and bottom edges are * excluded. * * The @alphagamma parameter specifies that the alpha compositing be done * in a gamma-corrected color space. Since the source image is opaque RGB, * this argument only affects the edges. In the current implementation, * it is ignored. * * The @level parameter specifies the speed/quality tradeoff of the * image interpolation. Currently, only ART_FILTER_NEAREST is * implemented. **/ void art_rgb_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma) { /* Note: this is a slow implementation, and is missing all filter levels other than NEAREST. It is here for clarity of presentation and to establish the interface. */ int x, y; double inv[6]; art_u8 *dst_p, *dst_linestart; const art_u8 *src_p; ArtPoint pt, src_pt; int src_x, src_y; int run_x0, run_x1; dst_linestart = dst; art_affine_invert (inv, affine); for (y = y0; y < y1; y++) { pt.y = y + 0.5; run_x0 = x0; run_x1 = x1; art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height, inv); dst_p = dst_linestart + (run_x0 - x0) * 3; for (x = run_x0; x < run_x1; x++) { pt.x = x + 0.5; art_affine_point (&src_pt, &pt, inv); src_x = (int)floor (src_pt.x); src_y = (int)floor (src_pt.y); src_p = src + (src_y * src_rowstride) + src_x * 3; dst_p[0] = src_p[0]; dst_p[1] = src_p[1]; dst_p[2] = src_p[2]; dst_p += 3; } dst_linestart += dst_rowstride; } } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_affine.h000066400000000000000000000027171453236046100221430ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_AFFINE_H__ #define __ART_RGB_AFFINE_H__ /* This module handles compositing of affine-transformed rgb images over rgb pixel buffers. */ #ifdef LIBART_COMPILATION #include "art_filterlevel.h" #include "art_alphagamma.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif void art_rgb_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_affine_private.c000066400000000000000000000064431453236046100236700ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgb_affine_private.h" #include #include "art_misc.h" #include "art_point.h" #include "art_affine.h" /* Private functions for the rgb affine image compositors - primarily, the determination of runs, eliminating the need for source image bbox calculation in the inner loop. */ /* Determine a "run", such that the inverse affine of all pixels from (x0, y) inclusive to (x1, y) exclusive fit within the bounds of the source image. Initial values of x0, x1, and result values stored in first two pointer arguments. */ #define EPSILON 1e-6 void art_rgb_affine_run (int *p_x0, int *p_x1, int y, int src_width, int src_height, const double affine[6]) { int x0, x1; double z; double x_intercept; int xi; x0 = *p_x0; x1 = *p_x1; /* do left and right edges */ if (affine[0] > EPSILON) { z = affine[2] * (y + 0.5) + affine[4]; x_intercept = -z / affine[0]; xi = (int) ceil (x_intercept + EPSILON - 0.5); if (xi > x0) x0 = xi; x_intercept = (-z + src_width) / affine[0]; xi = (int) ceil (x_intercept - EPSILON - 0.5); if (xi < x1) x1 = xi; } else if (affine[0] < -EPSILON) { z = affine[2] * (y + 0.5) + affine[4]; x_intercept = (-z + src_width) / affine[0]; xi = (int) ceil (x_intercept + EPSILON - 0.5); if (xi > x0) x0 = xi; x_intercept = -z / affine[0]; xi = (int) ceil (x_intercept - EPSILON - 0.5); if (xi < x1) x1 = xi; } else { z = affine[2] * (y + 0.5) + affine[4]; if (z < 0 || z >= src_width) { *p_x1 = *p_x0; return; } } /* do top and bottom edges */ if (affine[1] > EPSILON) { z = affine[3] * (y + 0.5) + affine[5]; x_intercept = -z / affine[1]; xi = (int) ceil (x_intercept + EPSILON - 0.5); if (xi > x0) x0 = xi; x_intercept = (-z + src_height) / affine[1]; xi = (int) ceil (x_intercept - EPSILON - 0.5); if (xi < x1) x1 = xi; } else if (affine[1] < -EPSILON) { z = affine[3] * (y + 0.5) + affine[5]; x_intercept = (-z + src_height) / affine[1]; xi = (int) ceil (x_intercept + EPSILON - 0.5); if (xi > x0) x0 = xi; x_intercept = -z / affine[1]; xi = (int) ceil (x_intercept - EPSILON - 0.5); if (xi < x1) x1 = xi; } else { z = affine[3] * (y + 0.5) + affine[5]; if (z < 0 || z >= src_height) { *p_x1 = *p_x0; return; } } *p_x0 = x0; *p_x1 = x1; } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_affine_private.h000066400000000000000000000022711453236046100236700ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_AFFINE_PRIVATE_H__ #define __ART_RGB_AFFINE_PRIVATE_H__ /* This module handles compositing of affine-transformed rgb images over rgb pixel buffers. */ #ifdef __cplusplus extern "C" { #endif void art_rgb_affine_run (int *p_x0, int *p_x1, int y, int src_width, int src_height, const double affine[6]); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_bitmap_affine.c000066400000000000000000000140071453236046100234650ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgb_bitmap_affine.h" #include #include "art_misc.h" #include "art_point.h" #include "art_affine.h" #include "art_rgb_affine_private.h" /* This module handles compositing of affine-transformed bitmap images over rgb pixel buffers. */ /* Composite the source image over the destination image, applying the affine transform. Foreground color is given and assumed to be opaque, background color is assumed to be fully transparent. */ static void art_rgb_bitmap_affine_opaque (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, art_u32 rgb, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma) { /* Note: this is a slow implementation, and is missing all filter levels other than NEAREST. It is here for clarity of presentation and to establish the interface. */ int x, y; double inv[6]; art_u8 *dst_p, *dst_linestart; const art_u8 *src_p; ArtPoint pt, src_pt; int src_x, src_y; art_u8 r, g, b; int run_x0, run_x1; r = rgb >> 16; g = (rgb >> 8) & 0xff; b = rgb & 0xff; dst_linestart = dst; art_affine_invert (inv, affine); for (y = y0; y < y1; y++) { pt.y = y + 0.5; run_x0 = x0; run_x1 = x1; art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height, inv); dst_p = dst_linestart + (run_x0 - x0) * 3; for (x = run_x0; x < run_x1; x++) { pt.x = x + 0.5; art_affine_point (&src_pt, &pt, inv); src_x = (int)floor (src_pt.x); src_y = (int)floor (src_pt.y); src_p = src + (src_y * src_rowstride) + (src_x >> 3); if (*src_p & (128 >> (src_x & 7))) { dst_p[0] = r; dst_p[1] = g; dst_p[2] = b; } dst_p += 3; } dst_linestart += dst_rowstride; } } /* Composite the source image over the destination image, applying the affine transform. Foreground color is given, background color is assumed to be fully transparent. */ /** * art_rgb_bitmap_affine: Affine transform source bitmap image and composite. * @dst: Destination image RGB buffer. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @dst_rowstride: Rowstride of @dst buffer. * @src: Source image bitmap buffer. * @src_width: Width of source image. * @src_height: Height of source image. * @src_rowstride: Rowstride of @src buffer. * @rgba: RGBA foreground color, in 0xRRGGBBAA. * @affine: Affine transform. * @level: Filter level. * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing. * * Affine transform the source image stored in @src, compositing over * the area of destination image @dst specified by the rectangle * (@x0, @y0) - (@x1, @y1). * * The source bitmap stored with MSB as the leftmost pixel. Source 1 * bits correspond to the semitransparent color @rgba, while source 0 * bits are transparent. * * See art_rgb_affine() for a description of additional parameters. **/ void art_rgb_bitmap_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, art_u32 rgba, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma) { /* Note: this is a slow implementation, and is missing all filter levels other than NEAREST. It is here for clarity of presentation and to establish the interface. */ int x, y; double inv[6]; art_u8 *dst_p, *dst_linestart; const art_u8 *src_p; ArtPoint pt, src_pt; int src_x, src_y; int alpha; art_u8 bg_r, bg_g, bg_b; art_u8 fg_r, fg_g, fg_b; art_u8 r, g, b; int run_x0, run_x1; alpha = rgba & 0xff; if (alpha == 0xff) { art_rgb_bitmap_affine_opaque (dst, x0, y0, x1, y1, dst_rowstride, src, src_width, src_height, src_rowstride, rgba >> 8, affine, level, alphagamma); return; } /* alpha = (65536 * alpha) / 255; */ alpha = (alpha << 8) + alpha + (alpha >> 7); r = rgba >> 24; g = (rgba >> 16) & 0xff; b = (rgba >> 8) & 0xff; dst_linestart = dst; art_affine_invert (inv, affine); for (y = y0; y < y1; y++) { pt.y = y + 0.5; run_x0 = x0; run_x1 = x1; art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height, inv); dst_p = dst_linestart + (run_x0 - x0) * 3; for (x = run_x0; x < run_x1; x++) { pt.x = x + 0.5; art_affine_point (&src_pt, &pt, inv); src_x = (int)floor (src_pt.x); src_y = (int)floor (src_pt.y); src_p = src + (src_y * src_rowstride) + (src_x >> 3); if (*src_p & (128 >> (src_x & 7))) { bg_r = dst_p[0]; bg_g = dst_p[1]; bg_b = dst_p[2]; fg_r = bg_r + (((r - bg_r) * alpha + 0x8000) >> 16); fg_g = bg_g + (((g - bg_g) * alpha + 0x8000) >> 16); fg_b = bg_b + (((b - bg_b) * alpha + 0x8000) >> 16); dst_p[0] = fg_r; dst_p[1] = fg_g; dst_p[2] = fg_b; } dst_p += 3; } dst_linestart += dst_rowstride; } } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_bitmap_affine.h000066400000000000000000000030521453236046100234700ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_BITMAP_AFFINE_H__ #define __ART_RGB_BITMAP_AFFINE_H__ /* This module handles compositing of affine-transformed bitmap images over rgb pixel buffers. */ #ifdef LIBART_COMPILATION #include "art_filterlevel.h" #include "art_alphagamma.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif void art_rgb_bitmap_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, art_u32 rgba, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_pixbuf_affine.c000066400000000000000000000066171453236046100235160ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgb_pixbuf_affine.h" #include #include "art_misc.h" #include "art_point.h" #include "art_affine.h" #include "art_pixbuf.h" #include "art_rgb_affine.h" #include "art_rgb_affine.h" #include "art_rgb_rgba_affine.h" /* This module handles compositing of affine-transformed generic pixbuf images over rgb pixel buffers. */ /* Composite the source image over the destination image, applying the affine transform. */ /** * art_rgb_pixbuf_affine: Affine transform source RGB pixbuf and composite. * @dst: Destination image RGB buffer. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @dst_rowstride: Rowstride of @dst buffer. * @pixbuf: source image pixbuf. * @affine: Affine transform. * @level: Filter level. * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing. * * Affine transform the source image stored in @src, compositing over * the area of destination image @dst specified by the rectangle * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges * of this rectangle are included, and the right and bottom edges are * excluded. * * The @alphagamma parameter specifies that the alpha compositing be * done in a gamma-corrected color space. In the current * implementation, it is ignored. * * The @level parameter specifies the speed/quality tradeoff of the * image interpolation. Currently, only ART_FILTER_NEAREST is * implemented. **/ void art_rgb_pixbuf_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const ArtPixBuf *pixbuf, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma) { if (pixbuf->format != ART_PIX_RGB) { art_warn ("art_rgb_pixbuf_affine: need RGB format image\n"); return; } if (pixbuf->bits_per_sample != 8) { art_warn ("art_rgb_pixbuf_affine: need 8-bit sample data\n"); return; } if (pixbuf->n_channels != 3 + (pixbuf->has_alpha != 0)) { art_warn ("art_rgb_pixbuf_affine: need 8-bit sample data\n"); return; } if (pixbuf->has_alpha) art_rgb_rgba_affine (dst, x0, y0, x1, y1, dst_rowstride, pixbuf->pixels, pixbuf->width, pixbuf->height, pixbuf->rowstride, affine, level, alphagamma); else art_rgb_affine (dst, x0, y0, x1, y1, dst_rowstride, pixbuf->pixels, pixbuf->width, pixbuf->height, pixbuf->rowstride, affine, level, alphagamma); } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_pixbuf_affine.h000066400000000000000000000030421453236046100235100ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_PIXBUF_AFFINE_H__ #define __ART_RGB_PIXBUF_AFFINE_H__ /* This module handles compositing of affine-transformed generic pixbuf images over rgb pixel buffers. */ #ifdef LIBART_COMPILATION #include "art_filterlevel.h" #include "art_alphagamma.h" #include "art_pixbuf.h" #else #include #include #include #endif #ifdef __cplusplus extern "C" { #endif void art_rgb_pixbuf_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const ArtPixBuf *pixbuf, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_rgba_affine.c000066400000000000000000000105431453236046100231250ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgb_rgba_affine.h" #include #include "art_misc.h" #include "art_point.h" #include "art_affine.h" #include "art_rgb_affine_private.h" /* This module handles compositing of affine-transformed rgba images over rgb pixel buffers. */ /* Composite the source image over the destination image, applying the affine transform. */ /** * art_rgb_rgba_affine: Affine transform source RGBA image and composite. * @dst: Destination image RGB buffer. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @dst_rowstride: Rowstride of @dst buffer. * @src: Source image RGBA buffer. * @src_width: Width of source image. * @src_height: Height of source image. * @src_rowstride: Rowstride of @src buffer. * @affine: Affine transform. * @level: Filter level. * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing. * * Affine transform the source image stored in @src, compositing over * the area of destination image @dst specified by the rectangle * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges * of this rectangle are included, and the right and bottom edges are * excluded. * * The @alphagamma parameter specifies that the alpha compositing be * done in a gamma-corrected color space. In the current * implementation, it is ignored. * * The @level parameter specifies the speed/quality tradeoff of the * image interpolation. Currently, only ART_FILTER_NEAREST is * implemented. **/ void art_rgb_rgba_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma) { /* Note: this is a slow implementation, and is missing all filter levels other than NEAREST. It is here for clarity of presentation and to establish the interface. */ int x, y; double inv[6]; art_u8 *dst_p, *dst_linestart; const art_u8 *src_p; ArtPoint pt, src_pt; int src_x, src_y; int alpha; art_u8 bg_r, bg_g, bg_b; art_u8 fg_r, fg_g, fg_b; int tmp; int run_x0, run_x1; dst_linestart = dst; art_affine_invert (inv, affine); for (y = y0; y < y1; y++) { pt.y = y + 0.5; run_x0 = x0; run_x1 = x1; art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height, inv); dst_p = dst_linestart + (run_x0 - x0) * 3; for (x = run_x0; x < run_x1; x++) { pt.x = x + 0.5; art_affine_point (&src_pt, &pt, inv); src_x = (int)floor (src_pt.x); src_y = (int)floor (src_pt.y); src_p = src + (src_y * src_rowstride) + src_x * 4; if (src_x >= 0 && src_x < src_width && src_y >= 0 && src_y < src_height) { alpha = src_p[3]; if (alpha) { if (alpha == 255) { dst_p[0] = src_p[0]; dst_p[1] = src_p[1]; dst_p[2] = src_p[2]; } else { bg_r = dst_p[0]; bg_g = dst_p[1]; bg_b = dst_p[2]; tmp = (src_p[0] - bg_r) * alpha; fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8); tmp = (src_p[1] - bg_g) * alpha; fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8); tmp = (src_p[2] - bg_b) * alpha; fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8); dst_p[0] = fg_r; dst_p[1] = fg_g; dst_p[2] = fg_b; } } } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; } dst_p += 3; } dst_linestart += dst_rowstride; } } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_rgba_affine.h000066400000000000000000000027771453236046100231440ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_RGBA_AFFINE_H__ #define __ART_RGB_RGBA_AFFINE_H__ /* This module handles compositing of affine-transformed rgba images over rgb pixel buffers. */ #ifdef LIBART_COMPILATION #include "art_filterlevel.h" #include "art_alphagamma.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif void art_rgb_rgba_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, const double affine[6], ArtFilterLevel level, ArtAlphaGamma *alphagamma); #ifdef __cplusplus } #endif #endif rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_svp.c000066400000000000000000000267271453236046100215250ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Render a sorted vector path into an RGB buffer. */ #include "config.h" #include "art_rgb_svp.h" #include "art_svp.h" #include "art_svp_render_aa.h" #include "art_rgb.h" typedef struct _ArtRgbSVPData ArtRgbSVPData; typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData; struct _ArtRgbSVPData { art_u32 rgbtab[256]; art_u8 *buf; int rowstride; int x0, x1; }; struct _ArtRgbSVPAlphaData { int alphatab[256]; art_u8 r, g, b, alpha; art_u8 *buf; int rowstride; int x0, x1; }; static void art_rgb_svp_callback (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtRgbSVPData *data = (ArtRgbSVPData *)callback_data; art_u8 *linebuf; int run_x0, run_x1; art_u32 running_sum = start; art_u32 rgb; int x0, x1; int k; linebuf = data->buf; x0 = data->x0; x1 = data->x1; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0) { rgb = data->rgbtab[(running_sum >> 16) & 0xff]; art_rgb_fill_run (linebuf, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), run_x1 - x0); } for (k = 0; k < n_steps - 1; k++) { running_sum += steps[k].delta; run_x0 = run_x1; run_x1 = steps[k + 1].x; if (run_x1 > run_x0) { rgb = data->rgbtab[(running_sum >> 16) & 0xff]; art_rgb_fill_run (linebuf + (run_x0 - x0) * 3, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), run_x1 - run_x0); } } running_sum += steps[k].delta; if (x1 > run_x1) { rgb = data->rgbtab[(running_sum >> 16) & 0xff]; art_rgb_fill_run (linebuf + (run_x1 - x0) * 3, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), x1 - run_x1); } } else { rgb = data->rgbtab[(running_sum >> 16) & 0xff]; art_rgb_fill_run (linebuf, (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff), x1 - x0); } data->buf += data->rowstride; } /* Render the vector path into the RGB buffer. */ /** * art_rgb_svp_aa: Render sorted vector path into RGB buffer. * @svp: The source sorted vector path. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @fg_color: Foreground color in 0xRRGGBB format. * @bg_color: Background color in 0xRRGGBB format. * @buf: Destination RGB buffer. * @rowstride: Rowstride of @buf buffer. * @alphagamma: #ArtAlphaGamma for gamma-correcting the rendering. * * Renders the shape specified with @svp into the @buf RGB buffer. * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height, * of the rectangle rendered. The new pixels are stored starting at * the first byte of @buf. Thus, the @x0 and @y0 parameters specify * an offset within @svp, and may be tweaked as a way of doing * integer-pixel translations without fiddling with @svp itself. * * The @fg_color and @bg_color arguments specify the opaque colors to * be used for rendering. For pixels of entirely 0 winding-number, * @bg_color is used. For pixels of entirely 1 winding number, * @fg_color is used. In between, the color is interpolated based on * the fraction of the pixel with a winding number of 1. If * @alphagamma is NULL, then linear interpolation (in pixel counts) is * the default. Otherwise, the interpolation is as specified by * @alphagamma. **/ void art_rgb_svp_aa (const ArtSVP *svp, int x0, int y0, int x1, int y1, art_u32 fg_color, art_u32 bg_color, art_u8 *buf, int rowstride, ArtAlphaGamma *alphagamma) { ArtRgbSVPData data; int r_fg, g_fg, b_fg; int r_bg, g_bg, b_bg; int r, g, b; int dr, dg, db; int i; if (alphagamma == NULL) { r_fg = fg_color >> 16; g_fg = (fg_color >> 8) & 0xff; b_fg = fg_color & 0xff; r_bg = bg_color >> 16; g_bg = (bg_color >> 8) & 0xff; b_bg = bg_color & 0xff; r = (r_bg << 16) + 0x8000; g = (g_bg << 16) + 0x8000; b = (b_bg << 16) + 0x8000; dr = ((r_fg - r_bg) << 16) / 255; dg = ((g_fg - g_bg) << 16) / 255; db = ((b_fg - b_bg) << 16) / 255; for (i = 0; i < 256; i++) { data.rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16); r += dr; g += dg; b += db; } } else { int *table; art_u8 *invtab; table = alphagamma->table; r_fg = table[fg_color >> 16]; g_fg = table[(fg_color >> 8) & 0xff]; b_fg = table[fg_color & 0xff]; r_bg = table[bg_color >> 16]; g_bg = table[(bg_color >> 8) & 0xff]; b_bg = table[bg_color & 0xff]; r = (r_bg << 16) + 0x8000; g = (g_bg << 16) + 0x8000; b = (b_bg << 16) + 0x8000; dr = ((r_fg - r_bg) << 16) / 255; dg = ((g_fg - g_bg) << 16) / 255; db = ((b_fg - b_bg) << 16) / 255; invtab = alphagamma->invtable; for (i = 0; i < 256; i++) { data.rgbtab[i] = (invtab[r >> 16] << 16) | (invtab[g >> 16] << 8) | invtab[b >> 16]; r += dr; g += dg; b += db; } } data.buf = buf; data.rowstride = rowstride; data.x0 = x0; data.x1 = x1; art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_callback, &data); } static void art_rgb_svp_alpha_callback (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data; art_u8 *linebuf; int run_x0, run_x1; art_u32 running_sum = start; int x0, x1; int k; art_u8 r, g, b; int *alphatab; int alpha; linebuf = data->buf; x0 = data->x0; x1 = data->x1; r = data->r; g = data->g; b = data->b; alphatab = data->alphatab; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0) { alpha = (running_sum >> 16) & 0xff; if (alpha) art_rgb_run_alpha (linebuf, r, g, b, alphatab[alpha], run_x1 - x0); } for (k = 0; k < n_steps - 1; k++) { running_sum += steps[k].delta; run_x0 = run_x1; run_x1 = steps[k + 1].x; if (run_x1 > run_x0) { alpha = (running_sum >> 16) & 0xff; if (alpha) art_rgb_run_alpha (linebuf + (run_x0 - x0) * 3, r, g, b, alphatab[alpha], run_x1 - run_x0); } } running_sum += steps[k].delta; if (x1 > run_x1) { alpha = (running_sum >> 16) & 0xff; if (alpha) art_rgb_run_alpha (linebuf + (run_x1 - x0) * 3, r, g, b, alphatab[alpha], x1 - run_x1); } } else { alpha = (running_sum >> 16) & 0xff; if (alpha) art_rgb_run_alpha (linebuf, r, g, b, alphatab[alpha], x1 - x0); } data->buf += data->rowstride; } static void art_rgb_svp_alpha_opaque_callback (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data; art_u8 *linebuf; int run_x0, run_x1; art_u32 running_sum = start; int x0, x1; int k; art_u8 r, g, b; int *alphatab; int alpha; linebuf = data->buf; x0 = data->x0; x1 = data->x1; r = data->r; g = data->g; b = data->b; alphatab = data->alphatab; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0) { alpha = running_sum >> 16; if (alpha) { if (alpha >= 255) art_rgb_fill_run (linebuf, r, g, b, run_x1 - x0); else art_rgb_run_alpha (linebuf, r, g, b, alphatab[alpha], run_x1 - x0); } } for (k = 0; k < n_steps - 1; k++) { running_sum += steps[k].delta; run_x0 = run_x1; run_x1 = steps[k + 1].x; if (run_x1 > run_x0) { alpha = running_sum >> 16; if (alpha) { if (alpha >= 255) art_rgb_fill_run (linebuf + (run_x0 - x0) * 3, r, g, b, run_x1 - run_x0); else art_rgb_run_alpha (linebuf + (run_x0 - x0) * 3, r, g, b, alphatab[alpha], run_x1 - run_x0); } } } running_sum += steps[k].delta; if (x1 > run_x1) { alpha = running_sum >> 16; if (alpha) { if (alpha >= 255) art_rgb_fill_run (linebuf + (run_x1 - x0) * 3, r, g, b, x1 - run_x1); else art_rgb_run_alpha (linebuf + (run_x1 - x0) * 3, r, g, b, alphatab[alpha], x1 - run_x1); } } } else { alpha = running_sum >> 16; if (alpha) { if (alpha >= 255) art_rgb_fill_run (linebuf, r, g, b, x1 - x0); else art_rgb_run_alpha (linebuf, r, g, b, alphatab[alpha], x1 - x0); } } data->buf += data->rowstride; } /** * art_rgb_svp_alpha: Alpha-composite sorted vector path over RGB buffer. * @svp: The source sorted vector path. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @rgba: Color in 0xRRGGBBAA format. * @buf: Destination RGB buffer. * @rowstride: Rowstride of @buf buffer. * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing. * * Renders the shape specified with @svp over the @buf RGB buffer. * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height, * of the rectangle rendered. The new pixels are stored starting at * the first byte of @buf. Thus, the @x0 and @y0 parameters specify * an offset within @svp, and may be tweaked as a way of doing * integer-pixel translations without fiddling with @svp itself. * * The @rgba argument specifies the color for the rendering. Pixels of * entirely 0 winding number are left untouched. Pixels of entirely * 1 winding number have the color @rgba composited over them (ie, * are replaced by the red, green, blue components of @rgba if the alpha * component is 0xff). Pixels of intermediate coverage are interpolated * according to the rule in @alphagamma, or default to linear if * @alphagamma is NULL. **/ void art_rgb_svp_alpha (const ArtSVP *svp, int x0, int y0, int x1, int y1, art_u32 rgba, art_u8 *buf, int rowstride, ArtAlphaGamma *alphagamma) { ArtRgbSVPAlphaData data; int r, g, b, alpha; int i; int a, da; r = rgba >> 24; g = (rgba >> 16) & 0xff; b = (rgba >> 8) & 0xff; alpha = rgba & 0xff; data.r = r; data.g = g; data.b = b; data.alpha = alpha; a = 0x8000; da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */ for (i = 0; i < 256; i++) { data.alphatab[i] = a >> 16; a += da; } data.buf = buf; data.rowstride = rowstride; data.x0 = x0; data.x1 = x1; if (alpha == 255) art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback, &data); else art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback, &data); } rl-renderpm-4.0.3/src/libart_lgpl/art_rgb_svp.h000066400000000000000000000030551453236046100215170ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGB_SVP_H__ #define __ART_RGB_SVP_H__ /* Render a sorted vector path into an RGB buffer. */ #ifdef LIBART_COMPILATION #include "art_alphagamma.h" #include "art_svp.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void art_rgb_svp_aa (const ArtSVP *svp, int x0, int y0, int x1, int y1, art_u32 fg_color, art_u32 bg_color, art_u8 *buf, int rowstride, ArtAlphaGamma *alphagamma); void art_rgb_svp_alpha (const ArtSVP *svp, int x0, int y0, int x1, int y1, art_u32 rgba, art_u8 *buf, int rowstride, ArtAlphaGamma *alphagamma); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_RGB_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_rgba.c000066400000000000000000000151001453236046100207550ustar00rootroot00000000000000/* * art_rgba.c: Functions for manipulating RGBA pixel data. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_rgba.h" #define ART_OPTIMIZE_SPACE #ifndef ART_OPTIMIZE_SPACE #include "art_rgba_table.c" #endif /** * art_rgba_rgba_composite: Composite RGBA image over RGBA buffer. * @dst: Destination RGBA buffer. * @src: Source RGBA buffer. * @n: Number of RGBA pixels to composite. * * Composites the RGBA pixels in @dst over the @src buffer. **/ void art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n) { int i; #ifdef WORDS_BIGENDIAN art_u32 src_rgba, dst_rgba; #else art_u32 src_abgr, dst_abgr; #endif art_u8 src_alpha, dst_alpha; for (i = 0; i < n; i++) { #ifdef WORDS_BIGENDIAN src_rgba = ((art_u32 *)src)[i]; src_alpha = src_rgba & 0xff; #else src_abgr = ((art_u32 *)src)[i]; src_alpha = (src_abgr >> 24) & 0xff; #endif if (src_alpha) { if (src_alpha == 0xff || ( #ifdef WORDS_BIGENDIAN dst_rgba = ((art_u32 *)dst)[i], dst_alpha = dst_rgba & 0xff, #else dst_abgr = ((art_u32 *)dst)[i], dst_alpha = (dst_abgr >> 24), #endif dst_alpha == 0)) #ifdef WORDS_BIGENDIAN ((art_u32 *)dst)[i] = src_rgba; #else ((art_u32 *)dst)[i] = src_abgr; #endif else { int r, g, b, a; int src_r, src_g, src_b; int dst_r, dst_g, dst_b; int tmp; int c; #ifdef ART_OPTIMIZE_SPACE tmp = (255 - src_alpha) * (255 - dst_alpha) + 0x80; a = 255 - ((tmp + (tmp >> 8)) >> 8); c = ((src_alpha << 16) + (a >> 1)) / a; #else tmp = art_rgba_composite_table[(src_alpha << 8) + dst_alpha]; c = tmp & 0x1ffff; a = tmp >> 24; #endif #ifdef WORDS_BIGENDIAN src_r = (src_rgba >> 24) & 0xff; src_g = (src_rgba >> 16) & 0xff; src_b = (src_rgba >> 8) & 0xff; dst_r = (dst_rgba >> 24) & 0xff; dst_g = (dst_rgba >> 16) & 0xff; dst_b = (dst_rgba >> 8) & 0xff; #else src_r = src_abgr & 0xff; src_g = (src_abgr >> 8) & 0xff; src_b = (src_abgr >> 16) & 0xff; dst_r = dst_abgr & 0xff; dst_g = (dst_abgr >> 8) & 0xff; dst_b = (dst_abgr >> 16) & 0xff; #endif r = dst_r + (((src_r - dst_r) * c + 0x8000) >> 16); g = dst_g + (((src_g - dst_g) * c + 0x8000) >> 16); b = dst_b + (((src_b - dst_b) * c + 0x8000) >> 16); #ifdef WORDS_BIGENDIAN ((art_u32 *)dst)[i] = (r << 24) | (g << 16) | (b << 8) | a; #else ((art_u32 *)dst)[i] = (a << 24) | (b << 16) | (g << 8) | r; #endif } } #if 0 /* it's not clear to me this optimization really wins */ else { /* skip over run of transparent pixels */ for (; i < n - 1; i++) { #ifdef WORDS_BIGENDIAN src_rgba = ((art_u32 *)src)[i + 1]; if (src_rgba & 0xff) break; #else src_abgr = ((art_u32 *)src)[i + 1]; if (src_abgr & 0xff000000) break; #endif } } #endif } } /** * art_rgba_fill_run: fill an RGBA buffer a solid RGB color. * @buf: Buffer to fill. * @r: Red, range 0..255. * @g: Green, range 0..255. * @b: Blue, range 0..255. * @n: Number of RGB triples to fill. * * Fills a buffer with @n copies of the (@r, @g, @b) triple, solid * alpha. Thus, locations @buf (inclusive) through @buf + 4 * @n * (exclusive) are written. **/ void art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n) { int i; #ifdef WORDS_BIGENDIAN art_u32 src_rgba; #else art_u32 src_abgr; #endif #ifdef WORDS_BIGENDIAN src_rgba = (r << 24) | (g << 16) | (b << 8) | 255; #else src_abgr = (255 << 24) | (b << 16) | (g << 8) | r; #endif for (i = 0; i < n; i++) { #ifdef WORDS_BIGENDIAN ((art_u32 *)buf)[i] = src_rgba; #else ((art_u32 *)buf)[i] = src_abgr; #endif } } /** * art_rgba_run_alpha: Render semitransparent color over RGBA buffer. * @buf: Buffer for rendering. * @r: Red, range 0..255. * @g: Green, range 0..255. * @b: Blue, range 0..255. * @alpha: Alpha, range 0..255. * @n: Number of RGB triples to render. * * Renders a sequential run of solid (@r, @g, @b) color over @buf with * opacity @alpha. Note that the range of @alpha is 0..255, in contrast * to art_rgb_run_alpha, which has a range of 0..256. **/ void art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n) { int i; #ifdef WORDS_BIGENDIAN art_u32 src_rgba, dst_rgba; #else art_u32 src_abgr, dst_abgr; #endif art_u8 dst_alpha; int a; int dst_r, dst_g, dst_b; int tmp; int c; #ifdef WORDS_BIGENDIAN src_rgba = (r << 24) | (g << 16) | (b << 8) | alpha; #else src_abgr = (alpha << 24) | (b << 16) | (g << 8) | r; #endif for (i = 0; i < n; i++) { #ifdef WORDS_BIGENDIAN dst_rgba = ((art_u32 *)buf)[i]; dst_alpha = dst_rgba & 0xff; #else dst_abgr = ((art_u32 *)buf)[i]; dst_alpha = (dst_abgr >> 24) & 0xff; #endif if (dst_alpha) { #ifdef ART_OPTIMIZE_SPACE tmp = (255 - alpha) * (255 - dst_alpha) + 0x80; a = 255 - ((tmp + (tmp >> 8)) >> 8); c = ((alpha << 16) + (a >> 1)) / a; #else tmp = art_rgba_composite_table[(alpha << 8) + dst_alpha]; c = tmp & 0x1ffff; a = tmp >> 24; #endif #ifdef WORDS_BIGENDIAN dst_r = (dst_rgba >> 24) & 0xff; dst_g = (dst_rgba >> 16) & 0xff; dst_b = (dst_rgba >> 8) & 0xff; #else dst_r = dst_abgr & 0xff; dst_g = (dst_abgr >> 8) & 0xff; dst_b = (dst_abgr >> 16) & 0xff; #endif dst_r += (((r - dst_r) * c + 0x8000) >> 16); dst_g += (((g - dst_g) * c + 0x8000) >> 16); dst_b += (((b - dst_b) * c + 0x8000) >> 16); #ifdef WORDS_BIGENDIAN ((art_u32 *)buf)[i] = (dst_r << 24) | (dst_g << 16) | (dst_b << 8) | a; #else ((art_u32 *)buf)[i] = (a << 24) | (dst_b << 16) | (dst_g << 8) | dst_r; #endif } else { #ifdef WORDS_BIGENDIAN ((art_u32 *)buf)[i] = src_rgba; #else ((art_u32 *)buf)[i] = src_abgr; #endif } } } rl-renderpm-4.0.3/src/libart_lgpl/art_rgba.h000066400000000000000000000025571453236046100207760ustar00rootroot00000000000000/* * art_rgba.h: Functions for manipulating RGBA pixel data. * * Libart_LGPL - library of basic graphic primitives * Copyright (C) 2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_RGBA_H__ #define __ART_RGBA_H__ #ifdef LIBART_COMPILATION #include "art_misc.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n); void art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n); void art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n); #ifdef __cplusplus } #endif /* __cplusplus */ #endif rl-renderpm-4.0.3/src/libart_lgpl/art_svp.c000066400000000000000000000105011453236046100206520ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Basic constructors and operations for sorted vector paths */ #include "config.h" #include "art_svp.h" #include "art_misc.h" /* Add a new segment. The arguments can be zero and NULL if the caller would rather fill them in later. We also realloc one auxiliary array of ints of size n_segs if desired. */ /** * art_svp_add_segment: Add a segment to an #ArtSVP structure. * @p_vp: Pointer to where the #ArtSVP structure is stored. * @pn_segs_max: Pointer to the allocated size of *@p_vp. * @pn_points_max: Pointer to where auxiliary array is stored. * @n_points: Number of points for new segment. * @dir: Direction for new segment; 0 is up, 1 is down. * @points: Points for new segment. * @bbox: Bounding box for new segment. * * Adds a new segment to an ArtSVP structure. This routine reallocates * the structure if necessary, updating *@p_vp and *@pn_segs_max as * necessary. * * The new segment is simply added after all other segments. Thus, * this routine should be called in order consistent with the #ArtSVP * sorting rules. * * If the @bbox argument is given, it is simply stored in the new * segment. Otherwise (if it is NULL), the bounding box is computed * from the @points given. **/ int art_svp_add_segment (ArtSVP **p_vp, int *pn_segs_max, int **pn_points_max, int n_points, int dir, ArtPoint *points, ArtDRect *bbox) { int seg_num; ArtSVP *svp; ArtSVPSeg *seg; svp = *p_vp; seg_num = svp->n_segs++; if (*pn_segs_max == seg_num) { *pn_segs_max <<= 1; svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) + (*pn_segs_max - 1) * sizeof(ArtSVPSeg)); *p_vp = svp; if (pn_points_max != NULL) *pn_points_max = art_renew (*pn_points_max, int, *pn_segs_max); } seg = &svp->segs[seg_num]; seg->n_points = n_points; seg->dir = dir; seg->points = points; if (bbox) seg->bbox = *bbox; else if (points) { double x_min, x_max; int i; x_min = x_max = points[0].x; for (i = 1; i < n_points; i++) { if (x_min > points[i].x) x_min = points[i].x; if (x_max < points[i].x) x_max = points[i].x; } seg->bbox.x0 = x_min; seg->bbox.y0 = points[0].y; seg->bbox.x1 = x_max; seg->bbox.y1 = points[n_points - 1].y; } return seg_num; } /** * art_svp_free: Free an #ArtSVP structure. * @svp: #ArtSVP to free. * * Frees an #ArtSVP structure and all the segments in it. **/ void art_svp_free (ArtSVP *svp) { int n_segs = svp->n_segs; int i; for (i = 0; i < n_segs; i++) art_free (svp->segs[i].points); art_free (svp); } #ifdef ART_USE_NEW_INTERSECTOR #define EPSILON 0 #else #define EPSILON 1e-6 #endif /** * art_svp_seg_compare: Compare two segments of an svp. * @seg1: First segment to compare. * @seg2: Second segment to compare. * * Compares two segments of an svp. Return 1 if @seg2 is below or to the * right of @seg1, -1 otherwise. **/ int art_svp_seg_compare (const void *s1, const void *s2) { const ArtSVPSeg *seg1 = s1; const ArtSVPSeg *seg2 = s2; if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1; else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1; else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1; else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1; else if ((seg1->points[1].x - seg1->points[0].x) * (seg2->points[1].y - seg2->points[0].y) - (seg1->points[1].y - seg1->points[0].y) * (seg2->points[1].x - seg2->points[0].x) > 0) return 1; else return -1; } rl-renderpm-4.0.3/src/libart_lgpl/art_svp.h000066400000000000000000000033101453236046100206570ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_H__ #define __ART_SVP_H__ /* Basic data structures and constructors for sorted vector paths */ #ifdef LIBART_COMPILATION #include "art_rect.h" #include "art_point.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtSVP ArtSVP; typedef struct _ArtSVPSeg ArtSVPSeg; struct _ArtSVPSeg { int n_points; int dir; /* == 0 for "up", 1 for "down" */ ArtDRect bbox; ArtPoint *points; }; struct _ArtSVP { int n_segs; ArtSVPSeg segs[1]; }; int art_svp_add_segment (ArtSVP **p_vp, int *pn_segs_max, int **pn_points_max, int n_points, int dir, ArtPoint *points, ArtDRect *bbox); void art_svp_free (ArtSVP *svp); int art_svp_seg_compare (const void *s1, const void *s2); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_intersect.c000066400000000000000000001316531453236046100227460ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 2001 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* This file contains a testbed implementation of the new intersection code. */ #include "config.h" #include "art_svp_intersect.h" #include /* for sqrt */ /* Sanitychecking verifies the main invariant on every priority queue point. Do not use in production, as it slows things down way too much. */ #define noSANITYCHECK /* This can be used in production, to prevent hangs. Eventually, it should not be necessary. */ #define CHEAP_SANITYCHECK #define noVERBOSE #include "art_misc.h" /* A priority queue - perhaps move to a separate file if it becomes needed somewhere else */ #define ART_PRIQ_USE_HEAP typedef struct _ArtPriQ ArtPriQ; typedef struct _ArtPriPoint ArtPriPoint; struct _ArtPriQ { int n_items; int n_items_max; ArtPriPoint **items; }; struct _ArtPriPoint { double x; double y; void *user_data; }; static ArtPriQ * art_pri_new (void) { ArtPriQ *result = art_new (ArtPriQ, 1); result->n_items = 0; result->n_items_max = 16; result->items = art_new (ArtPriPoint *, result->n_items_max); return result; } static void art_pri_free (ArtPriQ *pq) { art_free (pq->items); art_free (pq); } static art_boolean art_pri_empty (ArtPriQ *pq) { return pq->n_items == 0; } #ifdef ART_PRIQ_USE_HEAP /* This heap implementation is based on Vasek Chvatal's course notes: http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */ static void art_pri_bubble_up (ArtPriQ *pq, int vacant, ArtPriPoint *missing) { ArtPriPoint **items = pq->items; int parent; parent = (vacant - 1) >> 1; while (vacant > 0 && (missing->y < items[parent]->y || (missing->y == items[parent]->y && missing->x < items[parent]->x))) { items[vacant] = items[parent]; vacant = parent; parent = (vacant - 1) >> 1; } items[vacant] = missing; } static void art_pri_insert (ArtPriQ *pq, ArtPriPoint *point) { if (pq->n_items == pq->n_items_max) art_expand (pq->items, ArtPriPoint *, pq->n_items_max); art_pri_bubble_up (pq, pq->n_items++, point); } static void art_pri_sift_down_from_root (ArtPriQ *pq, ArtPriPoint *missing) { ArtPriPoint **items = pq->items; int vacant = 0, child = 2; int n = pq->n_items; while (child < n) { if (items[child - 1]->y < items[child]->y || (items[child - 1]->y == items[child]->y && items[child - 1]->x < items[child]->x)) child--; items[vacant] = items[child]; vacant = child; child = (vacant + 1) << 1; } if (child == n) { items[vacant] = items[n - 1]; vacant = n - 1; } art_pri_bubble_up (pq, vacant, missing); } static ArtPriPoint * art_pri_choose (ArtPriQ *pq) { ArtPriPoint *result = pq->items[0]; art_pri_sift_down_from_root (pq, pq->items[--pq->n_items]); return result; } #else /* Choose least point in queue */ static ArtPriPoint * art_pri_choose (ArtPriQ *pq) { int i; int best = 0; double best_x, best_y; double y; ArtPriPoint *result; if (pq->n_items == 0) return NULL; best_x = pq->items[best]->x; best_y = pq->items[best]->y; for (i = 1; i < pq->n_items; i++) { y = pq->items[i]->y; if (y < best_y || (y == best_y && pq->items[i]->x < best_x)) { best = i; best_x = pq->items[best]->x; best_y = y; } } result = pq->items[best]; pq->items[best] = pq->items[--pq->n_items]; return result; } static void art_pri_insert (ArtPriQ *pq, ArtPriPoint *point) { if (pq->n_items == pq->n_items_max) art_expand (pq->items, ArtPriPoint *, pq->n_items_max); pq->items[pq->n_items++] = point; } #endif #ifdef TEST_PRIQ #include /* for rand() */ #include static double double_rand (double lo, double hi, int quant) { int tmp = rand () / (RAND_MAX * (1.0 / quant)) + 0.5; return lo + tmp * ((hi - lo) / quant); } /* * This custom allocator for priority queue points is here so I can * test speed. It doesn't look like it will be that significant, but * if I want a small improvement later, it's something. */ typedef ArtPriPoint *ArtPriPtPool; static ArtPriPtPool * art_pri_pt_pool_new (void) { ArtPriPtPool *result = art_new (ArtPriPtPool, 1); *result = NULL; return result; } static ArtPriPoint * art_pri_pt_alloc (ArtPriPtPool *pool) { ArtPriPoint *result = *pool; if (result == NULL) return art_new (ArtPriPoint, 1); else { *pool = result->user_data; return result; } } static void art_pri_pt_free (ArtPriPtPool *pool, ArtPriPoint *pt) { pt->user_data = *pool; *pool = pt; } static void art_pri_pt_pool_free (ArtPriPtPool *pool) { ArtPriPoint *pt = *pool; while (pt != NULL) { ArtPriPoint *next = pt->user_data; art_free (pt); pt = next; } art_free (pool); } int main (int argc, char **argv) { ArtPriPtPool *pool = art_pri_pt_pool_new (); ArtPriQ *pq; int i, j; const int n_iter = 1; const int pq_size = 100; for (j = 0; j < n_iter; j++) { pq = art_pri_new (); for (i = 0; i < pq_size; i++) { ArtPriPoint *pt = art_pri_pt_alloc (pool); pt->x = double_rand (0, 1, 100); pt->y = double_rand (0, 1, 100); pt->user_data = (void *)i; art_pri_insert (pq, pt); } while (!art_pri_empty (pq)) { ArtPriPoint *pt = art_pri_choose (pq); if (n_iter == 1) printf ("(%g, %g), %d\n", pt->x, pt->y, (int)pt->user_data); art_pri_pt_free (pool, pt); } art_pri_free (pq); } art_pri_pt_pool_free (pool); return 0; } #else /* TEST_PRIQ */ /* A virtual class for an "svp writer". A client of this object creates an SVP by repeatedly calling "add segment" and "add point" methods on it. */ typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind; /* An implementation of the svp writer virtual class that applies the winding rule. */ struct _ArtSvpWriterRewind { ArtSvpWriter super; ArtWindRule rule; ArtSVP *svp; int n_segs_max; int *n_points_max; }; static int art_svp_writer_rewind_add_segment (ArtSvpWriter *self, int wind_left, int delta_wind, double x, double y) { ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self; ArtSVP *svp; ArtSVPSeg *seg; art_boolean left_filled, right_filled; int wind_right = wind_left + delta_wind; int seg_num; const int init_n_points_max = 4; switch (swr->rule) { case ART_WIND_RULE_NONZERO: left_filled = (wind_left != 0); right_filled = (wind_right != 0); break; case ART_WIND_RULE_INTERSECT: left_filled = (wind_left > 1); right_filled = (wind_right > 1); break; case ART_WIND_RULE_ODDEVEN: left_filled = (wind_left & 1); right_filled = (wind_right & 1); break; case ART_WIND_RULE_POSITIVE: left_filled = (wind_left > 0); right_filled = (wind_right > 0); break; default: art_die ("Unknown wind rule %d\n", swr->rule); } if (left_filled == right_filled) { /* discard segment now */ #ifdef VERBOSE art_dprint ("swr add_segment: %d += %d (%g, %g) --> -1\n", wind_left, delta_wind, x, y); #endif return -1; } svp = swr->svp; seg_num = svp->n_segs++; if (swr->n_segs_max == seg_num) { swr->n_segs_max <<= 1; svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) + (swr->n_segs_max - 1) * sizeof(ArtSVPSeg)); swr->svp = svp; swr->n_points_max = art_renew (swr->n_points_max, int, swr->n_segs_max); } seg = &svp->segs[seg_num]; seg->n_points = 1; seg->dir = right_filled; swr->n_points_max[seg_num] = init_n_points_max; seg->bbox.x0 = x; seg->bbox.y0 = y; seg->bbox.x1 = x; seg->bbox.y1 = y; seg->points = art_new (ArtPoint, init_n_points_max); seg->points[0].x = x; seg->points[0].y = y; #ifdef VERBOSE art_dprint ("swr add_segment: %d += %d (%g, %g) --> %d(%s)\n", wind_left, delta_wind, x, y, seg_num, seg->dir ? "v" : "^"); #endif return seg_num; } static void art_svp_writer_rewind_add_point (ArtSvpWriter *self, int seg_id, double x, double y) { ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self; ArtSVPSeg *seg; int n_points; #ifdef VERBOSE art_dprint ("swr add_point: %d (%g, %g)\n", seg_id, x, y); #endif if (seg_id < 0) /* omitted segment */ return; seg = &swr->svp->segs[seg_id]; n_points = seg->n_points++; if (swr->n_points_max[seg_id] == n_points) art_expand (seg->points, ArtPoint, swr->n_points_max[seg_id]); seg->points[n_points].x = x; seg->points[n_points].y = y; if (x < seg->bbox.x0) seg->bbox.x0 = x; if (x > seg->bbox.x1) seg->bbox.x1 = x; seg->bbox.y1 = y; } static void art_svp_writer_rewind_close_segment (ArtSvpWriter *self, int seg_id) { /* Not needed for this simple implementation. A potential future optimization is to merge segments that can be merged safely. */ #ifdef SANITYCHECK ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self; ArtSVPSeg *seg; if (seg_id >= 0) { seg = &swr->svp->segs[seg_id]; if (seg->n_points < 2) art_warn ("*** closing segment %d with only %d point%s\n", seg_id, seg->n_points, seg->n_points == 1 ? "" : "s"); } #endif #ifdef VERBOSE art_dprint ("swr close_segment: %d\n", seg_id); #endif } ArtSVP * art_svp_writer_rewind_reap (ArtSvpWriter *self) { ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self; ArtSVP *result = swr->svp; art_free (swr->n_points_max); art_free (swr); return result; } ArtSvpWriter * art_svp_writer_rewind_new (ArtWindRule rule) { ArtSvpWriterRewind *result = art_new (ArtSvpWriterRewind, 1); result->super.add_segment = art_svp_writer_rewind_add_segment; result->super.add_point = art_svp_writer_rewind_add_point; result->super.close_segment = art_svp_writer_rewind_close_segment; result->rule = rule; result->n_segs_max = 16; result->svp = art_alloc (sizeof(ArtSVP) + (result->n_segs_max - 1) * sizeof(ArtSVPSeg)); result->svp->n_segs = 0; result->n_points_max = art_new (int, result->n_segs_max); return &result->super; } /* Now, data structures for the active list */ typedef struct _ArtActiveSeg ArtActiveSeg; /* Note: BNEG is 1 for \ lines, and 0 for /. Thus, x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */ #define ART_ACTIVE_FLAGS_BNEG 1 /* This flag is set if the segment has been inserted into the active list. */ #define ART_ACTIVE_FLAGS_IN_ACTIVE 2 /* This flag is set when the segment is to be deleted in the horiz commit process. */ #define ART_ACTIVE_FLAGS_DEL 4 /* This flag is set if the seg_id is a valid output segment. */ #define ART_ACTIVE_FLAGS_OUT 8 /* This flag is set if the segment is in the horiz list. */ #define ART_ACTIVE_FLAGS_IN_HORIZ 16 struct _ArtActiveSeg { int flags; int wind_left, delta_wind; ArtActiveSeg *left, *right; /* doubly linked list structure */ const ArtSVPSeg *in_seg; int in_curs; double x[2]; double y0, y1; double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1, and a>0 */ /* bottom point and intersection point stack */ int n_stack; int n_stack_max; ArtPoint *stack; /* horiz commit list */ ArtActiveSeg *horiz_left, *horiz_right; double horiz_x; int horiz_delta_wind; int seg_id; }; typedef struct _ArtIntersectCtx ArtIntersectCtx; struct _ArtIntersectCtx { const ArtSVP *in; ArtSvpWriter *out; ArtPriQ *pq; ArtActiveSeg *active_head; double y; ArtActiveSeg *horiz_first; ArtActiveSeg *horiz_last; /* segment index of next input segment to be added to pri q */ int in_curs; }; #define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */ /** * art_svp_intersect_setup_seg: Set up an active segment from input segment. * @seg: Active segment. * @pri_pt: Priority queue point to initialize. * * Sets the x[], a, b, c, flags, and stack fields according to the * line from the current cursor value. Sets the priority queue point * to the bottom point of this line. Also advances the input segment * cursor. **/ static void art_svp_intersect_setup_seg (ArtActiveSeg *seg, ArtPriPoint *pri_pt) { const ArtSVPSeg *in_seg = seg->in_seg; int in_curs = seg->in_curs++; double x0, y0, x1, y1; double dx, dy, s; double a, b, r2; x0 = in_seg->points[in_curs].x; y0 = in_seg->points[in_curs].y; x1 = in_seg->points[in_curs + 1].x; y1 = in_seg->points[in_curs + 1].y; pri_pt->x = x1; pri_pt->y = y1; dx = x1 - x0; dy = y1 - y0; r2 = dx * dx + dy * dy; s = r2 == 0 ? 1 : 1 / sqrt (r2); seg->a = a = dy * s; seg->b = b = -dx * s; seg->c = -(a * x0 + b * y0); seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0); seg->x[0] = x0; seg->x[1] = x1; seg->y0 = y0; seg->y1 = y1; seg->n_stack = 1; seg->stack[0].x = x1; seg->stack[0].y = y1; } /** * art_svp_intersect_add_horiz: Add point to horizontal list. * @ctx: Intersector context. * @seg: Segment with point to insert into horizontal list. * * Inserts @seg into horizontal list, keeping it in ascending horiz_x * order. * * Note: the horiz_commit routine processes "clusters" of segs in the * horiz list, all sharing the same horiz_x value. The cluster is * processed in active list order, rather than horiz list order. Thus, * the order of segs in the horiz list sharing the same horiz_x * _should_ be irrelevant. Even so, we use b as a secondary sorting key, * as a "belt and suspenders" defensive coding tactic. **/ static void art_svp_intersect_add_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg) { ArtActiveSeg **pp = &ctx->horiz_last; ArtActiveSeg *place; ArtActiveSeg *place_right = NULL; #ifdef CHEAP_SANITYCHECK if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ) { art_warn ("*** attempt to put segment in horiz list twice\n"); return; } seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ; #endif #ifdef VERBOSE art_dprint ("add_horiz %lx, x = %g\n", (unsigned long) seg, seg->horiz_x); #endif for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x || (place->horiz_x == seg->horiz_x && place->b < seg->b)); place = *pp) { place_right = place; pp = &place->horiz_left; } *pp = seg; seg->horiz_left = place; seg->horiz_right = place_right; if (place == NULL) ctx->horiz_first = seg; else place->horiz_right = seg; } static void art_svp_intersect_push_pt (ArtIntersectCtx *ctx, ArtActiveSeg *seg, double x, double y) { ArtPriPoint *pri_pt; int n_stack = seg->n_stack; if (n_stack == seg->n_stack_max) art_expand (seg->stack, ArtPoint, seg->n_stack_max); seg->stack[n_stack].x = x; seg->stack[n_stack].y = y; seg->n_stack++; seg->x[1] = x; seg->y1 = y; pri_pt = art_new (ArtPriPoint, 1); pri_pt->x = x; pri_pt->y = y; pri_pt->user_data = seg; art_pri_insert (ctx->pq, pri_pt); } typedef enum { ART_BREAK_LEFT = 1, ART_BREAK_RIGHT = 2 } ArtBreakFlags; /** * art_svp_intersect_break: Break an active segment. * * Note: y must be greater than the top point's y, and less than * the bottom's. * * Return value: x coordinate of break point. */ static double art_svp_intersect_break (ArtIntersectCtx *ctx, ArtActiveSeg *seg, double x_ref, double y, ArtBreakFlags break_flags) { double x0, y0, x1, y1; const ArtSVPSeg *in_seg = seg->in_seg; int in_curs = seg->in_curs; double x; x0 = in_seg->points[in_curs - 1].x; y0 = in_seg->points[in_curs - 1].y; x1 = in_seg->points[in_curs].x; y1 = in_seg->points[in_curs].y; x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0)); if ((break_flags == ART_BREAK_LEFT && x > x_ref) || (break_flags == ART_BREAK_RIGHT && x < x_ref)) { #ifdef VERBOSE art_dprint ("art_svp_intersect_break: limiting x to %f, was %f, %s\n", x_ref, x, break_flags == ART_BREAK_LEFT ? "left" : "right"); x = x_ref; #endif } /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane arithmetic, but it might be worthwhile to check just in case. */ if (y > ctx->y) art_svp_intersect_push_pt (ctx, seg, x, y); else { seg->x[0] = x; seg->y0 = y; seg->horiz_x = x; art_svp_intersect_add_horiz (ctx, seg); } return x; } /** * art_svp_intersect_add_point: Add a point, breaking nearby neighbors. * @ctx: Intersector context. * @x: X coordinate of point to add. * @y: Y coordinate of point to add. * @seg: "nearby" segment, or NULL if leftmost. * * Return value: Segment immediately to the left of the new point, or * NULL if the new point is leftmost. **/ static ArtActiveSeg * art_svp_intersect_add_point (ArtIntersectCtx *ctx, double x, double y, ArtActiveSeg *seg, ArtBreakFlags break_flags) { ArtActiveSeg *left, *right; double x_min = x, x_max = x; art_boolean left_live, right_live; double d; double new_x; ArtActiveSeg *test, *result = NULL; double x_test; left = seg; if (left == NULL) right = ctx->active_head; else right = left->right; left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL); right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL); while (left_live || right_live) { if (left_live) { if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] && /* It may be that one of these conjuncts turns out to be always true. We test both anyway, to be defensive. */ y != left->y0 && y < left->y1) { d = x_min * left->a + y * left->b + left->c; if (d < EPSILON_A) { new_x = art_svp_intersect_break (ctx, left, x_min, y, ART_BREAK_LEFT); if (new_x > x_max) { x_max = new_x; right_live = (right != NULL); } else if (new_x < x_min) x_min = new_x; left = left->left; left_live = (left != NULL); } else left_live = ART_FALSE; } else left_live = ART_FALSE; } else if (right_live) { if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] && /* It may be that one of these conjuncts turns out to be always true. We test both anyway, to be defensive. */ y != right->y0 && y < right->y1) { d = x_max * right->a + y * right->b + right->c; if (d > -EPSILON_A) { new_x = art_svp_intersect_break (ctx, right, x_max, y, ART_BREAK_RIGHT); if (new_x < x_min) { x_min = new_x; left_live = (left != NULL); } else if (new_x >= x_max) x_max = new_x; right = right->right; right_live = (right != NULL); } else right_live = ART_FALSE; } else right_live = ART_FALSE; } } /* Ascending order is guaranteed by break_flags. Thus, we don't need to actually fix up non-ascending pairs. */ /* Now, (left, right) defines an interval of segments broken. Sort into ascending x order. */ test = left == NULL ? ctx->active_head : left->right; result = left; if (test != NULL && test != right) { if (y == test->y0) x_test = test->x[0]; else /* assert y == test->y1, I think */ x_test = test->x[1]; for (;;) { if (x_test <= x) result = test; test = test->right; if (test == right) break; new_x = x_test; if (new_x < x_test) { art_warn ("art_svp_intersect_add_point: non-ascending x\n"); } x_test = new_x; } } return result; } static void art_svp_intersect_swap_active (ArtIntersectCtx *ctx, ArtActiveSeg *left_seg, ArtActiveSeg *right_seg) { right_seg->left = left_seg->left; if (right_seg->left != NULL) right_seg->left->right = right_seg; else ctx->active_head = right_seg; left_seg->right = right_seg->right; if (left_seg->right != NULL) left_seg->right->left = left_seg; left_seg->left = right_seg; right_seg->right = left_seg; } /** * art_svp_intersect_test_cross: Test crossing of a pair of active segments. * @ctx: Intersector context. * @left_seg: Left segment of the pair. * @right_seg: Right segment of the pair. * @break_flags: Flags indicating whether to break neighbors. * * Tests crossing of @left_seg and @right_seg. If there is a crossing, * inserts the intersection point into both segments. * * Return value: True if the intersection took place at the current * scan line, indicating further iteration is needed. **/ static art_boolean art_svp_intersect_test_cross (ArtIntersectCtx *ctx, ArtActiveSeg *left_seg, ArtActiveSeg *right_seg, ArtBreakFlags break_flags) { double left_x0, left_y0, left_x1; double left_y1 = left_seg->y1; double right_y1 = right_seg->y1; double d; const ArtSVPSeg *in_seg; int in_curs; double d0, d1, t; double x, y; /* intersection point */ #ifdef VERBOSE static int count = 0; art_dprint ("art_svp_intersect_test_cross %lx <-> %lx: count=%d\n", (unsigned long)left_seg, (unsigned long)right_seg, count++); #endif if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0]) { /* Top points of left and right segments coincide. This case feels like a bit of duplication - we may want to merge it with the cases below. However, this way, we're sure that this logic makes only localized changes. */ if (left_y1 < right_y1) { /* Test left (x1, y1) against right segment */ double left_x1 = left_seg->x[1]; if (left_x1 < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] || left_y1 == right_seg->y0) return ART_FALSE; d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c; if (d < -EPSILON_A) return ART_FALSE; else if (d < EPSILON_A) { /* I'm unsure about the break flags here. */ double right_x1 = art_svp_intersect_break (ctx, right_seg, left_x1, left_y1, ART_BREAK_RIGHT); if (left_x1 <= right_x1) return ART_FALSE; } } else if (left_y1 > right_y1) { /* Test right (x1, y1) against left segment */ double right_x1 = right_seg->x[1]; if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] || right_y1 == left_seg->y0) return ART_FALSE; d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c; if (d > EPSILON_A) return ART_FALSE; else if (d > -EPSILON_A) { /* See above regarding break flags. */ double left_x1 = art_svp_intersect_break (ctx, left_seg, right_x1, right_y1, ART_BREAK_LEFT); if (left_x1 <= right_x1) return ART_FALSE; } } else /* left_y1 == right_y1 */ { double left_x1 = left_seg->x[1]; double right_x1 = right_seg->x[1]; if (left_x1 <= right_x1) return ART_FALSE; } art_svp_intersect_swap_active (ctx, left_seg, right_seg); return ART_TRUE; } if (left_y1 < right_y1) { /* Test left (x1, y1) against right segment */ double left_x1 = left_seg->x[1]; if (left_x1 < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] || left_y1 == right_seg->y0) return ART_FALSE; d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c; if (d < -EPSILON_A) return ART_FALSE; else if (d < EPSILON_A) { double right_x1 = art_svp_intersect_break (ctx, right_seg, left_x1, left_y1, ART_BREAK_RIGHT); if (left_x1 <= right_x1) return ART_FALSE; } } else if (left_y1 > right_y1) { /* Test right (x1, y1) against left segment */ double right_x1 = right_seg->x[1]; if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] || right_y1 == left_seg->y0) return ART_FALSE; d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c; if (d > EPSILON_A) return ART_FALSE; else if (d > -EPSILON_A) { double left_x1 = art_svp_intersect_break (ctx, left_seg, right_x1, right_y1, ART_BREAK_LEFT); if (left_x1 <= right_x1) return ART_FALSE; } } else /* left_y1 == right_y1 */ { double left_x1 = left_seg->x[1]; double right_x1 = right_seg->x[1]; if (left_x1 <= right_x1) return ART_FALSE; } /* The segments cross. Find the intersection point. */ in_seg = left_seg->in_seg; in_curs = left_seg->in_curs; left_x0 = in_seg->points[in_curs - 1].x; left_y0 = in_seg->points[in_curs - 1].y; left_x1 = in_seg->points[in_curs].x; left_y1 = in_seg->points[in_curs].y; d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c; d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c; if (d0 == d1) { x = left_x0; y = left_y0; } else { /* Is this division always safe? It could possibly overflow. */ t = d0 / (d0 - d1); if (t <= 0) { x = left_x0; y = left_y0; } else if (t >= 1) { x = left_x1; y = left_y1; } else { x = left_x0 + t * (left_x1 - left_x0); y = left_y0 + t * (left_y1 - left_y0); } } /* Make sure intersection point is within bounds of right seg. */ if (y < right_seg->y0) { x = right_seg->x[0]; y = right_seg->y0; } else if (y > right_seg->y1) { x = right_seg->x[1]; y = right_seg->y1; } else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]) x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]; else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG]) x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG]; if (y == left_seg->y0) { if (y != right_seg->y0) { #ifdef VERBOSE art_dprint ("art_svp_intersect_test_cross: intersection (%g, %g) matches former y0 of %lx, %lx\n", x, y, (unsigned long)left_seg, (unsigned long)right_seg); #endif art_svp_intersect_push_pt (ctx, right_seg, x, y); if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL) art_svp_intersect_add_point (ctx, x, y, right_seg->right, break_flags); } else { /* Intersection takes place at current scan line; process immediately rather than queueing intersection point into priq. */ ArtActiveSeg *winner, *loser; /* Choose "most vertical" segement */ if (left_seg->a > right_seg->a) { winner = left_seg; loser = right_seg; } else { winner = right_seg; loser = left_seg; } loser->x[0] = winner->x[0]; loser->horiz_x = loser->x[0]; loser->horiz_delta_wind += loser->delta_wind; winner->horiz_delta_wind -= loser->delta_wind; art_svp_intersect_swap_active (ctx, left_seg, right_seg); return ART_TRUE; } } else if (y == right_seg->y0) { #ifdef VERBOSE art_dprint ("*** art_svp_intersect_test_cross: intersection (%g, %g) matches latter y0 of %lx, %lx\n", x, y, (unsigned long)left_seg, (unsigned long)right_seg); #endif art_svp_intersect_push_pt (ctx, left_seg, x, y); if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL) art_svp_intersect_add_point (ctx, x, y, left_seg->left, break_flags); } else { #ifdef VERBOSE art_dprint ("Inserting (%g, %g) into %lx, %lx\n", x, y, (unsigned long)left_seg, (unsigned long)right_seg); #endif /* Insert the intersection point into both segments. */ art_svp_intersect_push_pt (ctx, left_seg, x, y); art_svp_intersect_push_pt (ctx, right_seg, x, y); if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL) art_svp_intersect_add_point (ctx, x, y, left_seg->left, break_flags); if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL) art_svp_intersect_add_point (ctx, x, y, right_seg->right, break_flags); } return ART_FALSE; } /** * art_svp_intersect_active_delete: Delete segment from active list. * @ctx: Intersection context. * @seg: Segment to delete. * * Deletes @seg from the active list. **/ static /* todo inline */ void art_svp_intersect_active_delete (ArtIntersectCtx *ctx, ArtActiveSeg *seg) { ArtActiveSeg *left = seg->left, *right = seg->right; if (left != NULL) left->right = right; else ctx->active_head = right; if (right != NULL) right->left = left; } /** * art_svp_intersect_active_free: Free an active segment. * @seg: Segment to delete. * * Frees @seg. **/ static /* todo inline */ void art_svp_intersect_active_free (ArtActiveSeg *seg) { art_free (seg->stack); #ifdef VERBOSE art_dprint ("Freeing %lx\n", (unsigned long) seg); #endif art_free (seg); } /** * art_svp_intersect_insert_cross: Test crossings of newly inserted line. * * Tests @seg against its left and right neighbors for intersections. * Precondition: the line in @seg is not purely horizontal. **/ static void art_svp_intersect_insert_cross (ArtIntersectCtx *ctx, ArtActiveSeg *seg) { ArtActiveSeg *left = seg, *right = seg; for (;;) { if (left != NULL) { ArtActiveSeg *leftc; for (leftc = left->left; leftc != NULL; leftc = leftc->left) if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL)) break; if (leftc != NULL && art_svp_intersect_test_cross (ctx, leftc, left, ART_BREAK_LEFT)) { if (left == right || right == NULL) right = left->right; } else { left = NULL; } } else if (right != NULL && right->right != NULL) { ArtActiveSeg *rightc; for (rightc = right->right; rightc != NULL; rightc = rightc->right) if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL)) break; if (rightc != NULL && art_svp_intersect_test_cross (ctx, right, rightc, ART_BREAK_RIGHT)) { if (left == right || left == NULL) left = right->left; } else { right = NULL; } } else break; } } /** * art_svp_intersect_horiz: Add horizontal line segment. * @ctx: Intersector context. * @seg: Segment on which to add horizontal line. * @x0: Old x position. * @x1: New x position. * * Adds a horizontal line from @x0 to @x1, and updates the current * location of @seg to @x1. **/ static void art_svp_intersect_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg, double x0, double x1) { ArtActiveSeg *hs; if (x0 == x1) return; hs = art_new (ArtActiveSeg, 1); hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT); if (seg->flags & ART_ACTIVE_FLAGS_OUT) { ArtSvpWriter *swr = ctx->out; swr->add_point (swr, seg->seg_id, x0, ctx->y); } hs->seg_id = seg->seg_id; hs->horiz_x = x0; hs->horiz_delta_wind = seg->delta_wind; hs->stack = NULL; /* Ideally, the (a, b, c) values will never be read. However, there are probably some tests remaining that don't check for _DEL before evaluating the line equation. For those, these initializations will at least prevent a UMR of the values, which can crash on some platforms. */ hs->a = 0.0; hs->b = 0.0; hs->c = 0.0; seg->horiz_delta_wind -= seg->delta_wind; art_svp_intersect_add_horiz (ctx, hs); if (x0 > x1) { ArtActiveSeg *left; art_boolean first = ART_TRUE; for (left = seg->left; left != NULL; left = seg->left) { int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG; if (left->x[left_bneg] <= x1) break; if (left->x[left_bneg ^ 1] <= x1 && x1 * left->a + ctx->y * left->b + left->c >= 0) break; if (left->y0 != ctx->y && left->y1 != ctx->y) { art_svp_intersect_break (ctx, left, x1, ctx->y, ART_BREAK_LEFT); } #ifdef VERBOSE art_dprint ("x0=%g > x1=%g, swapping %lx, %lx\n", x0, x1, (unsigned long)left, (unsigned long)seg); #endif art_svp_intersect_swap_active (ctx, left, seg); if (first && left->right != NULL) { art_svp_intersect_test_cross (ctx, left, left->right, ART_BREAK_RIGHT); first = ART_FALSE; } } } else { ArtActiveSeg *right; art_boolean first = ART_TRUE; for (right = seg->right; right != NULL; right = seg->right) { int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG; if (right->x[right_bneg ^ 1] >= x1) break; if (right->x[right_bneg] >= x1 && x1 * right->a + ctx->y * right->b + right->c <= 0) break; if (right->y0 != ctx->y && right->y1 != ctx->y) { art_svp_intersect_break (ctx, right, x1, ctx->y, ART_BREAK_LEFT); } #ifdef VERBOSE art_dprint ("[right]x0=%g < x1=%g, swapping %lx, %lx\n", x0, x1, (unsigned long)seg, (unsigned long)right); #endif art_svp_intersect_swap_active (ctx, seg, right); if (first && right->left != NULL) { art_svp_intersect_test_cross (ctx, right->left, right, ART_BREAK_RIGHT); first = ART_FALSE; } } } seg->x[0] = x1; seg->x[1] = x1; seg->horiz_x = x1; seg->flags &= ~ART_ACTIVE_FLAGS_OUT; } /** * art_svp_intersect_insert_line: Insert a line into the active list. * @ctx: Intersector context. * @seg: Segment containing line to insert. * * Inserts the line into the intersector context, taking care of any * intersections, and adding the appropriate horizontal points to the * active list. **/ static void art_svp_intersect_insert_line (ArtIntersectCtx *ctx, ArtActiveSeg *seg) { if (seg->y1 == seg->y0) { #ifdef VERBOSE art_dprint ("art_svp_intersect_insert_line: %lx is horizontal\n", (unsigned long)seg); #endif art_svp_intersect_horiz (ctx, seg, seg->x[0], seg->x[1]); } else { art_svp_intersect_insert_cross (ctx, seg); art_svp_intersect_add_horiz (ctx, seg); } } static void art_svp_intersect_process_intersection (ArtIntersectCtx *ctx, ArtActiveSeg *seg) { int n_stack = --seg->n_stack; seg->x[1] = seg->stack[n_stack - 1].x; seg->y1 = seg->stack[n_stack - 1].y; seg->x[0] = seg->stack[n_stack].x; seg->y0 = seg->stack[n_stack].y; seg->horiz_x = seg->x[0]; art_svp_intersect_insert_line (ctx, seg); } static void art_svp_intersect_advance_cursor (ArtIntersectCtx *ctx, ArtActiveSeg *seg, ArtPriPoint *pri_pt) { const ArtSVPSeg *in_seg = seg->in_seg; int in_curs = seg->in_curs; ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL; if (swr != NULL) swr->add_point (swr, seg->seg_id, seg->x[1], seg->y1); if (in_curs + 1 == in_seg->n_points) { ArtActiveSeg *left = seg->left, *right = seg->right; #if 0 if (swr != NULL) swr->close_segment (swr, seg->seg_id); seg->flags &= ~ART_ACTIVE_FLAGS_OUT; #endif seg->flags |= ART_ACTIVE_FLAGS_DEL; art_svp_intersect_add_horiz (ctx, seg); art_svp_intersect_active_delete (ctx, seg); if (left != NULL && right != NULL) art_svp_intersect_test_cross (ctx, left, right, ART_BREAK_LEFT | ART_BREAK_RIGHT); art_free (pri_pt); } else { seg->horiz_x = seg->x[1]; art_svp_intersect_setup_seg (seg, pri_pt); art_pri_insert (ctx->pq, pri_pt); art_svp_intersect_insert_line (ctx, seg); } } static void art_svp_intersect_add_seg (ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg) { ArtActiveSeg *seg = art_new (ArtActiveSeg, 1); ArtActiveSeg *test; double x0, y0; ArtActiveSeg *beg_range; ArtActiveSeg *last = NULL; ArtActiveSeg *left, *right; ArtPriPoint *pri_pt = art_new (ArtPriPoint, 1); seg->flags = 0; seg->in_seg = in_seg; seg->in_curs = 0; seg->n_stack_max = 4; seg->stack = art_new (ArtPoint, seg->n_stack_max); seg->horiz_delta_wind = 0; seg->wind_left = 0; pri_pt->user_data = seg; art_svp_intersect_setup_seg (seg, pri_pt); art_pri_insert (ctx->pq, pri_pt); /* Find insertion place for new segment */ /* This is currently a left-to-right scan, but should be replaced with a binary search as soon as it's validated. */ x0 = in_seg->points[0].x; y0 = in_seg->points[0].y; beg_range = NULL; for (test = ctx->active_head; test != NULL; test = test->right) { double d; int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG; if (x0 < test->x[test_bneg]) { if (x0 < test->x[test_bneg ^ 1]) break; d = x0 * test->a + y0 * test->b + test->c; if (d < 0) break; } last = test; } left = art_svp_intersect_add_point (ctx, x0, y0, last, ART_BREAK_LEFT | ART_BREAK_RIGHT); seg->left = left; if (left == NULL) { right = ctx->active_head; ctx->active_head = seg; } else { right = left->right; left->right = seg; } seg->right = right; if (right != NULL) right->left = seg; seg->delta_wind = in_seg->dir ? 1 : -1; seg->horiz_x = x0; art_svp_intersect_insert_line (ctx, seg); } #ifdef SANITYCHECK static void art_svp_intersect_sanitycheck_winding (ArtIntersectCtx *ctx) { #if 0 /* At this point, we seem to be getting false positives, so it's turned off for now. */ ArtActiveSeg *seg; int winding_number = 0; for (seg = ctx->active_head; seg != NULL; seg = seg->right) { /* Check winding number consistency. */ if (seg->flags & ART_ACTIVE_FLAGS_OUT) { if (winding_number != seg->wind_left) art_warn ("*** art_svp_intersect_sanitycheck_winding: seg %lx has wind_left of %d, expected %d\n", (unsigned long) seg, seg->wind_left, winding_number); winding_number = seg->wind_left + seg->delta_wind; } } if (winding_number != 0) art_warn ("*** art_svp_intersect_sanitycheck_winding: non-balanced winding number %d\n", winding_number); #endif } #endif /** * art_svp_intersect_horiz_commit: Commit points in horiz list to output. * @ctx: Intersection context. * * The main function of the horizontal commit is to output new * points to the output writer. * * This "commit" pass is also where winding numbers are assigned, * because doing it here provides much greater tolerance for inputs * which are not in strict SVP order. * * Each cluster in the horiz_list contains both segments that are in * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not, * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We * need to deal with both. **/ static void art_svp_intersect_horiz_commit (ArtIntersectCtx *ctx) { ArtActiveSeg *seg; int winding_number = 0; /* initialization just to avoid warning */ int horiz_wind = 0; double last_x = 0; /* initialization just to avoid warning */ #ifdef VERBOSE art_dprint ("art_svp_intersect_horiz_commit: y=%g\n", ctx->y); for (seg = ctx->horiz_first; seg != NULL; seg = seg->horiz_right) art_dprint (" %lx: %g %+d\n", (unsigned long)seg, seg->horiz_x, seg->horiz_delta_wind); #endif /* Output points to svp writer. */ for (seg = ctx->horiz_first; seg != NULL;) { /* Find a cluster with common horiz_x, */ ArtActiveSeg *curs; double x = seg->horiz_x; /* Generate any horizontal segments. */ if (horiz_wind != 0) { ArtSvpWriter *swr = ctx->out; int seg_id; seg_id = swr->add_segment (swr, winding_number, horiz_wind, last_x, ctx->y); swr->add_point (swr, seg_id, x, ctx->y); swr->close_segment (swr, seg_id); } /* Find first active segment in cluster. */ for (curs = seg; curs != NULL && curs->horiz_x == x; curs = curs->horiz_right) if (!(curs->flags & ART_ACTIVE_FLAGS_DEL)) break; if (curs != NULL && curs->horiz_x == x) { /* There exists at least one active segment in this cluster. */ /* Find beginning of cluster. */ for (; curs->left != NULL; curs = curs->left) if (curs->left->horiz_x != x) break; if (curs->left != NULL) winding_number = curs->left->wind_left + curs->left->delta_wind; else winding_number = 0; do { #ifdef VERBOSE art_dprint (" curs %lx: winding_number = %d += %d\n", (unsigned long)curs, winding_number, curs->delta_wind); #endif if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) || curs->wind_left != winding_number) { ArtSvpWriter *swr = ctx->out; if (curs->flags & ART_ACTIVE_FLAGS_OUT) { swr->add_point (swr, curs->seg_id, curs->horiz_x, ctx->y); swr->close_segment (swr, curs->seg_id); } curs->seg_id = swr->add_segment (swr, winding_number, curs->delta_wind, x, ctx->y); curs->flags |= ART_ACTIVE_FLAGS_OUT; } curs->wind_left = winding_number; winding_number += curs->delta_wind; curs = curs->right; } while (curs != NULL && curs->horiz_x == x); } /* Skip past cluster. */ do { ArtActiveSeg *next = seg->horiz_right; seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ; horiz_wind += seg->horiz_delta_wind; seg->horiz_delta_wind = 0; if (seg->flags & ART_ACTIVE_FLAGS_DEL) { if (seg->flags & ART_ACTIVE_FLAGS_OUT) { ArtSvpWriter *swr = ctx->out; swr->close_segment (swr, seg->seg_id); } art_svp_intersect_active_free (seg); } seg = next; } while (seg != NULL && seg->horiz_x == x); last_x = x; } ctx->horiz_first = NULL; ctx->horiz_last = NULL; #ifdef SANITYCHECK art_svp_intersect_sanitycheck_winding (ctx); #endif } #ifdef VERBOSE static void art_svp_intersect_print_active (ArtIntersectCtx *ctx) { ArtActiveSeg *seg; art_dprint ("Active list (y = %g):\n", ctx->y); for (seg = ctx->active_head; seg != NULL; seg = seg->right) { art_dprint (" %lx: (%g, %g)-(%g, %g), (a, b, c) = (%g, %g, %g)\n", (unsigned long)seg, seg->x[0], seg->y0, seg->x[1], seg->y1, seg->a, seg->b, seg->c); } } #endif #ifdef SANITYCHECK static void art_svp_intersect_sanitycheck (ArtIntersectCtx *ctx) { ArtActiveSeg *seg; ArtActiveSeg *last = NULL; double d; for (seg = ctx->active_head; seg != NULL; seg = seg->right) { if (seg->left != last) { art_warn ("*** art_svp_intersect_sanitycheck: last=%lx, seg->left=%lx\n", (unsigned long)last, (unsigned long)seg->left); } if (last != NULL) { /* pairwise compare with previous seg */ /* First the top. */ if (last->y0 < seg->y0) { } else { } /* Then the bottom. */ if (last->y1 < seg->y1) { if (!((last->x[1] < seg->x[(seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]) || last->y1 == seg->y0)) { d = last->x[1] * seg->a + last->y1 * seg->b + seg->c; if (d >= -EPSILON_A) art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to right (d = %g)\n", last->x[1], last->y1, (unsigned long) last, (unsigned long) seg, d); } } else if (last->y1 > seg->y1) { if (!((seg->x[1] > last->x[last->flags & ART_ACTIVE_FLAGS_BNEG]) || seg->y1 == last->y0)) { d = seg->x[1] * last->a + seg->y1 * last->b + last->c; if (d <= EPSILON_A) art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to left (d = %g)\n", seg->x[1], seg->y1, (unsigned long) seg, (unsigned long) last, d); } } else { if (last->x[1] > seg->x[1]) art_warn ("*** bottoms (%g, %g) of %lx and (%g, %g) of %lx out of order\n", last->x[1], last->y1, (unsigned long)last, seg->x[1], seg->y1, (unsigned long)seg); } } last = seg; } } #endif void art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out) { ArtIntersectCtx *ctx; ArtPriQ *pq; ArtPriPoint *first_point; #ifdef VERBOSE int count = 0; #endif if (in->n_segs == 0) return; ctx = art_new (ArtIntersectCtx, 1); ctx->in = in; ctx->out = out; pq = art_pri_new (); ctx->pq = pq; ctx->active_head = NULL; ctx->horiz_first = NULL; ctx->horiz_last = NULL; ctx->in_curs = 0; first_point = art_new (ArtPriPoint, 1); first_point->x = in->segs[0].points[0].x; first_point->y = in->segs[0].points[0].y; first_point->user_data = NULL; ctx->y = first_point->y; art_pri_insert (pq, first_point); while (!art_pri_empty (pq)) { ArtPriPoint *pri_point = art_pri_choose (pq); ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data; #ifdef VERBOSE art_dprint ("\nIntersector step %d\n", count++); art_svp_intersect_print_active (ctx); art_dprint ("priq choose (%g, %g) %lx\n", pri_point->x, pri_point->y, (unsigned long)pri_point->user_data); #endif #ifdef SANITYCHECK art_svp_intersect_sanitycheck(ctx); #endif if (ctx->y != pri_point->y) { art_svp_intersect_horiz_commit (ctx); ctx->y = pri_point->y; } if (seg == NULL) { /* Insert new segment from input */ const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++]; art_svp_intersect_add_seg (ctx, in_seg); if (ctx->in_curs < in->n_segs) { const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs]; pri_point->x = next_seg->points[0].x; pri_point->y = next_seg->points[0].y; /* user_data is already NULL */ art_pri_insert (pq, pri_point); } else art_free (pri_point); } else { int n_stack = seg->n_stack; if (n_stack > 1) { art_svp_intersect_process_intersection (ctx, seg); art_free (pri_point); } else { art_svp_intersect_advance_cursor (ctx, seg, pri_point); } } } art_svp_intersect_horiz_commit (ctx); art_pri_free (pq); art_free (ctx); } #endif /* not TEST_PRIQ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_intersect.h000066400000000000000000000036051453236046100227460ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 2001 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_INTERSECT_H__ #define __ART_SVP_INTERSECT_H__ /* The funky new SVP intersector. */ #ifdef LIBART_COMPILATION #include "art_svp.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef ART_WIND_RULE_DEFINED #define ART_WIND_RULE_DEFINED typedef enum { ART_WIND_RULE_NONZERO, ART_WIND_RULE_INTERSECT, ART_WIND_RULE_ODDEVEN, ART_WIND_RULE_POSITIVE } ArtWindRule; #endif typedef struct _ArtSvpWriter ArtSvpWriter; struct _ArtSvpWriter { int (*add_segment) (ArtSvpWriter *self, int wind_left, int delta_wind, double x, double y); void (*add_point) (ArtSvpWriter *self, int seg_id, double x, double y); void (*close_segment) (ArtSvpWriter *self, int seg_id); }; ArtSvpWriter * art_svp_writer_rewind_new (ArtWindRule rule); ArtSVP * art_svp_writer_rewind_reap (ArtSvpWriter *self); int art_svp_seg_compare (const void *s1, const void *s2); void art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_INTERSECT_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_ops.c000066400000000000000000000270141453236046100215420ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #define noVERBOSE /* Vector path set operations, over sorted vpaths. */ #include "config.h" #include "art_svp_ops.h" #include "art_misc.h" #include "art_svp.h" #include "art_vpath.h" #include "art_svp_vpath.h" #include "art_svp.h" #ifdef ART_USE_NEW_INTERSECTOR #include "art_svp_intersect.h" #else #include "art_svp_wind.h" #endif #include "art_vpath_svp.h" /* Merge the segments of the two svp's. The resulting svp will share segments with args passed in, so be super-careful with the allocation. */ /** * art_svp_merge: Merge the segments of two svp's. * @svp1: One svp to merge. * @svp2: The other svp to merge. * * Merges the segments of two SVP's into a new one. The resulting * #ArtSVP data structure will share the segments of the argument * svp's, so it is probably a good idea to free it shallowly, * especially if the arguments will be freed with art_svp_free(). * * Return value: The merged #ArtSVP. **/ static ArtSVP * art_svp_merge (const ArtSVP *svp1, const ArtSVP *svp2) { ArtSVP *svp_new; int ix; int ix1, ix2; svp_new = (ArtSVP *)art_alloc (sizeof(ArtSVP) + (svp1->n_segs + svp2->n_segs - 1) * sizeof(ArtSVPSeg)); ix1 = 0; ix2 = 0; for (ix = 0; ix < svp1->n_segs + svp2->n_segs; ix++) { if (ix1 < svp1->n_segs && (ix2 == svp2->n_segs || art_svp_seg_compare (&svp1->segs[ix1], &svp2->segs[ix2]) < 1)) svp_new->segs[ix] = svp1->segs[ix1++]; else svp_new->segs[ix] = svp2->segs[ix2++]; } svp_new->n_segs = ix; return svp_new; } #ifdef VERBOSE #define XOFF 50 #define YOFF 700 static void print_ps_vpath (ArtVpath *vpath) { int i; printf ("gsave %d %d translate 1 -1 scale\n", XOFF, YOFF); for (i = 0; vpath[i].code != ART_END; i++) { switch (vpath[i].code) { case ART_MOVETO: printf ("%g %g moveto\n", vpath[i].x, vpath[i].y); break; case ART_LINETO: printf ("%g %g lineto\n", vpath[i].x, vpath[i].y); break; default: break; } } printf ("stroke grestore showpage\n"); } #define DELT 4 static void print_ps_svp (ArtSVP *vpath) { int i, j; printf ("%% begin\n"); for (i = 0; i < vpath->n_segs; i++) { printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0); for (j = 0; j < vpath->segs[i].n_points; j++) { printf ("%g %g %s\n", XOFF + vpath->segs[i].points[j].x, YOFF - vpath->segs[i].points[j].y, j ? "lineto" : "moveto"); } printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n", XOFF + vpath->segs[i].points[0].x - DELT, YOFF - DELT - vpath->segs[i].points[0].y, XOFF + vpath->segs[i].points[0].x - DELT, YOFF - vpath->segs[i].points[0].y, XOFF + vpath->segs[i].points[0].x + DELT, YOFF - vpath->segs[i].points[0].y, XOFF + vpath->segs[i].points[0].x + DELT, YOFF - DELT - vpath->segs[i].points[0].y); printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n", XOFF + vpath->segs[i].points[j - 1].x - DELT, YOFF + DELT - vpath->segs[i].points[j - 1].y, XOFF + vpath->segs[i].points[j - 1].x - DELT, YOFF - vpath->segs[i].points[j - 1].y, XOFF + vpath->segs[i].points[j - 1].x + DELT, YOFF - vpath->segs[i].points[j - 1].y, XOFF + vpath->segs[i].points[j - 1].x + DELT, YOFF + DELT - vpath->segs[i].points[j - 1].y); printf ("stroke\n"); } printf ("showpage\n"); } #endif #ifndef ART_USE_NEW_INTERSECTOR static ArtSVP * art_svp_merge_perturbed (const ArtSVP *svp1, const ArtSVP *svp2) { ArtVpath *vpath1, *vpath2; ArtVpath *vpath1_p, *vpath2_p; ArtSVP *svp1_p, *svp2_p; ArtSVP *svp_new; vpath1 = art_vpath_from_svp (svp1); vpath1_p = art_vpath_perturb (vpath1); art_free (vpath1); svp1_p = art_svp_from_vpath (vpath1_p); art_free (vpath1_p); vpath2 = art_vpath_from_svp (svp2); vpath2_p = art_vpath_perturb (vpath2); art_free (vpath2); svp2_p = art_svp_from_vpath (vpath2_p); art_free (vpath2_p); svp_new = art_svp_merge (svp1_p, svp2_p); #ifdef VERBOSE print_ps_svp (svp1_p); print_ps_svp (svp2_p); print_ps_svp (svp_new); #endif art_free (svp1_p); art_free (svp2_p); return svp_new; } #endif /* Compute the union of two vector paths. Status of this routine: Basic correctness: Seems to work. Numerical stability: We cheat (adding random perturbation). Thus, it seems very likely that no numerical stability problems will be seen in practice. Speed: Would be better if we didn't go to unsorted vector path and back to add the perturbation. Precision: The perturbation fuzzes the coordinates slightly. In cases of butting segments, razor thin long holes may appear. */ /** * art_svp_union: Compute the union of two sorted vector paths. * @svp1: One sorted vector path. * @svp2: The other sorted vector path. * * Computes the union of the two argument svp's. Given two svp's with * winding numbers of 0 and 1 everywhere, the resulting winding number * will be 1 where either (or both) of the argument svp's has a * winding number 1, 0 otherwise. The result is newly allocated. * * Currently, this routine has accuracy problems pending the * implementation of the new intersector. * * Return value: The union of @svp1 and @svp2. **/ ArtSVP * art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2) { #ifdef ART_USE_NEW_INTERSECTOR ArtSVP *svp3, *svp_new; ArtSvpWriter *swr; svp3 = art_svp_merge (svp1, svp2); swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE); art_svp_intersector (svp3, swr); svp_new = art_svp_writer_rewind_reap (swr); art_free (svp3); /* shallow free because svp3 contains shared segments */ return svp_new; #else ArtSVP *svp3, *svp4, *svp_new; svp3 = art_svp_merge_perturbed (svp1, svp2); svp4 = art_svp_uncross (svp3); art_svp_free (svp3); svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_POSITIVE); #ifdef VERBOSE print_ps_svp (svp4); print_ps_svp (svp_new); #endif art_svp_free (svp4); return svp_new; #endif } /* Compute the intersection of two vector paths. Status of this routine: Basic correctness: Seems to work. Numerical stability: We cheat (adding random perturbation). Thus, it seems very likely that no numerical stability problems will be seen in practice. Speed: Would be better if we didn't go to unsorted vector path and back to add the perturbation. Precision: The perturbation fuzzes the coordinates slightly. In cases of butting segments, razor thin long isolated segments may appear. */ /** * art_svp_intersect: Compute the intersection of two sorted vector paths. * @svp1: One sorted vector path. * @svp2: The other sorted vector path. * * Computes the intersection of the two argument svp's. Given two * svp's with winding numbers of 0 and 1 everywhere, the resulting * winding number will be 1 where both of the argument svp's has a * winding number 1, 0 otherwise. The result is newly allocated. * * Currently, this routine has accuracy problems pending the * implementation of the new intersector. * * Return value: The intersection of @svp1 and @svp2. **/ ArtSVP * art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2) { #ifdef ART_USE_NEW_INTERSECTOR ArtSVP *svp3, *svp_new; ArtSvpWriter *swr; #ifdef ROBIN_DEBUG dump_svp("art_svp_intersect svp1", svp1); dump_svp("art_svp_intersect svp2", svp2); #endif svp3 = art_svp_merge (svp1, svp2); swr = art_svp_writer_rewind_new (ART_WIND_RULE_INTERSECT); art_svp_intersector (svp3, swr); svp_new = art_svp_writer_rewind_reap (swr); art_free (svp3); /* shallow free because svp3 contains shared segments */ #ifdef ROBIN_DEBUG dump_svp("art_svp_intersect svp_new", svp_new); #endif return svp_new; #else ArtSVP *svp3, *svp4, *svp_new; svp3 = art_svp_merge_perturbed (svp1, svp2); svp4 = art_svp_uncross (svp3); art_svp_free (svp3); svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_INTERSECT); art_svp_free (svp4); return svp_new; #endif } /* Compute the symmetric difference of two vector paths. Status of this routine: Basic correctness: Seems to work. Numerical stability: We cheat (adding random perturbation). Thus, it seems very likely that no numerical stability problems will be seen in practice. Speed: We could do a lot better by scanning through the svp representations and culling out any segments that are exactly identical. It would also be better if we didn't go to unsorted vector path and back to add the perturbation. Precision: Awful. In the case of inputs which are similar (the common case for canvas display), the entire outline is "hairy." In addition, the perturbation fuzzes the coordinates slightly. It can be used as a conservative approximation. */ /** * art_svp_diff: Compute the symmetric difference of two sorted vector paths. * @svp1: One sorted vector path. * @svp2: The other sorted vector path. * * Computes the symmetric of the two argument svp's. Given two svp's * with winding numbers of 0 and 1 everywhere, the resulting winding * number will be 1 where either, but not both, of the argument svp's * has a winding number 1, 0 otherwise. The result is newly allocated. * * Currently, this routine has accuracy problems pending the * implementation of the new intersector. * * Return value: The symmetric difference of @svp1 and @svp2. **/ ArtSVP * art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2) { #ifdef ART_USE_NEW_INTERSECTOR ArtSVP *svp3, *svp_new; ArtSvpWriter *swr; svp3 = art_svp_merge (svp1, svp2); swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN); art_svp_intersector (svp3, swr); svp_new = art_svp_writer_rewind_reap (swr); art_free (svp3); /* shallow free because svp3 contains shared segments */ return svp_new; #else ArtSVP *svp3, *svp4, *svp_new; svp3 = art_svp_merge_perturbed (svp1, svp2); svp4 = art_svp_uncross (svp3); art_svp_free (svp3); svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_ODDEVEN); art_svp_free (svp4); return svp_new; #endif } #ifdef ART_USE_NEW_INTERSECTOR ArtSVP * art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2) { ArtSVP *svp2_mod; ArtSVP *svp3, *svp_new; ArtSvpWriter *swr; int i; svp2_mod = (ArtSVP *) svp2; /* get rid of the const for a while */ /* First invert svp2 to "turn it inside out" */ for (i = 0; i < svp2_mod->n_segs; i++) svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir; svp3 = art_svp_merge (svp1, svp2_mod); swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE); art_svp_intersector (svp3, swr); svp_new = art_svp_writer_rewind_reap (swr); art_free (svp3); /* shallow free because svp3 contains shared segments */ /* Flip svp2 back to its original state */ for (i = 0; i < svp2_mod->n_segs; i++) svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir; return svp_new; } #endif /* ART_USE_NEW_INTERSECTOR */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_ops.h000066400000000000000000000026321453236046100215460ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_OPS_H__ #define __ART_SVP_OPS_H__ #ifdef LIBART_COMPILATION #include "art_svp.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Vector path set operations, over sorted vpaths. */ ArtSVP *art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2); ArtSVP *art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2); ArtSVP *art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2); ArtSVP *art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_OPS_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_point.c000066400000000000000000000065141453236046100220740ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1999 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_svp_point.h" #include #include "art_misc.h" #include "art_svp.h" /* Determine whether a point is inside, or near, an svp. */ /* return winding number of point wrt svp */ /** * art_svp_point_wind: Determine winding number of a point with respect to svp. * @svp: The svp. * @x: The X coordinate of the point. * @y: The Y coordinate of the point. * * Determine the winding number of the point @x, @y with respect to @svp. * * Return value: the winding number. **/ int art_svp_point_wind (ArtSVP *svp, double x, double y) { int i, j; int wind = 0; for (i = 0; i < svp->n_segs; i++) { ArtSVPSeg *seg = &svp->segs[i]; if (seg->bbox.y0 > y) break; if (seg->bbox.y1 > y) { if (seg->bbox.x1 < x) wind += seg->dir ? 1 : -1; else if (seg->bbox.x0 <= x) { double x0, y0, x1, y1, dx, dy; for (j = 0; j < seg->n_points - 1; j++) { if (seg->points[j + 1].y > y) break; } x0 = seg->points[j].x; y0 = seg->points[j].y; x1 = seg->points[j + 1].x; y1 = seg->points[j + 1].y; dx = x1 - x0; dy = y1 - y0; if ((x - x0) * dy > (y - y0) * dx) wind += seg->dir ? 1 : -1; } } } return wind; } /** * art_svp_point_dist: Determine distance between point and svp. * @svp: The svp. * @x: The X coordinate of the point. * @y: The Y coordinate of the point. * * Determines the distance of the point @x, @y to the closest edge in * @svp. A large number is returned if @svp is empty. * * Return value: the distance. **/ double art_svp_point_dist (ArtSVP *svp, double x, double y) { int i, j; double dist_sq; double best_sq = -1; for (i = 0; i < svp->n_segs; i++) { ArtSVPSeg *seg = &svp->segs[i]; for (j = 0; j < seg->n_points - 1; j++) { double x0 = seg->points[j].x; double y0 = seg->points[j].y; double x1 = seg->points[j + 1].x; double y1 = seg->points[j + 1].y; double dx = x1 - x0; double dy = y1 - y0; double dxx0 = x - x0; double dyy0 = y - y0; double dot = dxx0 * dx + dyy0 * dy; if (dot < 0) dist_sq = dxx0 * dxx0 + dyy0 * dyy0; else { double rr = dx * dx + dy * dy; if (dot > rr) dist_sq = (x - x1) * (x - x1) + (y - y1) * (y - y1); else { double perp = (y - y0) * dx - (x - x0) * dy; dist_sq = perp * perp / rr; } } if (best_sq < 0 || dist_sq < best_sq) best_sq = dist_sq; } } if (best_sq >= 0) return sqrt (best_sq); else return 1e12; } rl-renderpm-4.0.3/src/libart_lgpl/art_svp_point.h000066400000000000000000000024311453236046100220730ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1999 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_POINT_H__ #define __ART_SVP_POINT_H__ /* Determine whether a point is inside, or near, an svp. */ #ifdef LIBART_COMPILATION #include "art_svp.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ int art_svp_point_wind (ArtSVP *svp, double x, double y); double art_svp_point_dist (ArtSVP *svp, double x, double y); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_render_aa.c000066400000000000000000000317121453236046100226610ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* The spiffy antialiased renderer for sorted vector paths. */ #include "config.h" #include "art_svp_render_aa.h" #include #include /* for memmove */ #include "art_misc.h" #include "art_rect.h" #include "art_svp.h" #include typedef double artfloat; struct _ArtSVPRenderAAIter { const ArtSVP *svp; int x0, x1; int y; int seg_ix; int *active_segs; int n_active_segs; int *cursor; artfloat *seg_x; artfloat *seg_dx; ArtSVPRenderAAStep *steps; }; static void art_svp_render_insert_active (int i, int *active_segs, int n_active_segs, artfloat *seg_x, artfloat *seg_dx) { int j; artfloat x; int tmp1, tmp2; /* this is a cheap hack to get ^'s sorted correctly */ x = seg_x[i] + 0.001 * seg_dx[i]; for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++); tmp1 = i; while (j < n_active_segs) { tmp2 = active_segs[j]; active_segs[j] = tmp1; tmp1 = tmp2; j++; } active_segs[j] = tmp1; } static void art_svp_render_delete_active (int *active_segs, int j, int n_active_segs) { int k; for (k = j; k < n_active_segs; k++) active_segs[k] = active_segs[k + 1]; } #define EPSILON 1e-6 /* Render the sorted vector path in the given rectangle, antialiased. This interface uses a callback for the actual pixel rendering. The callback is called y1 - y0 times (once for each scan line). The y coordinate is given as an argument for convenience (it could be stored in the callback's private data and incremented on each call). The rendered polygon is represented in a semi-runlength format: a start value and a sequence of "steps". Each step has an x coordinate and a value delta. The resulting value at position x is equal to the sum of the start value and all step delta values for which the step x coordinate is less than or equal to x. An efficient algorithm will traverse the steps left to right, keeping a running sum. All x coordinates in the steps are guaranteed to be x0 <= x < x1. (This guarantee is a change from the gfonted vpaar renderer, and is designed to simplify the callback). There is now a further guarantee that no two steps will have the same x value. This may allow for further speedup and simplification of renderers. The value 0x8000 represents 0% coverage by the polygon, while 0xff8000 represents 100% coverage. This format is designed so that >> 16 results in a standard 0x00..0xff value range, with nice rounding. Status of this routine: Basic correctness: OK Numerical stability: pretty good, although probably not bulletproof. Speed: Needs more aggressive culling of bounding boxes. Can probably speed up the [x0,x1) clipping of step values. Can do more of the step calculation in fixed point. Precision: No known problems, although it should be tested thoroughly, especially for symmetry. */ ArtSVPRenderAAIter * art_svp_render_aa_iter (const ArtSVP *svp, int x0, int y0, int x1, int y1) { ArtSVPRenderAAIter *iter = art_new (ArtSVPRenderAAIter, 1); iter->svp = svp; iter->y = y0; iter->x0 = x0; iter->x1 = x1; iter->seg_ix = 0; iter->active_segs = art_new (int, svp->n_segs); iter->cursor = art_new (int, svp->n_segs); iter->seg_x = art_new (artfloat, svp->n_segs); iter->seg_dx = art_new (artfloat, svp->n_segs); iter->steps = art_new (ArtSVPRenderAAStep, x1 - x0); iter->n_active_segs = 0; return iter; } #define ADD_STEP(xpos, xdelta) \ /* stereotype code fragment for adding a step */ \ if (n_steps == 0 || steps[n_steps - 1].x < xpos) \ { \ sx = n_steps; \ steps[sx].x = xpos; \ steps[sx].delta = xdelta; \ n_steps++; \ } \ else \ { \ for (sx = n_steps; sx > 0; sx--) \ { \ if (steps[sx - 1].x == xpos) \ { \ steps[sx - 1].delta += xdelta; \ sx = n_steps; \ break; \ } \ else if (steps[sx - 1].x < xpos) \ { \ break; \ } \ } \ if (sx < n_steps) \ { \ memmove (&steps[sx + 1], &steps[sx], \ (n_steps - sx) * sizeof(steps[0])); \ steps[sx].x = xpos; \ steps[sx].delta = xdelta; \ n_steps++; \ } \ } void art_svp_render_aa_iter_step (ArtSVPRenderAAIter *iter, int *p_start, ArtSVPRenderAAStep **p_steps, int *p_n_steps) { const ArtSVP *svp = iter->svp; int *active_segs = iter->active_segs; int n_active_segs = iter->n_active_segs; int *cursor = iter->cursor; artfloat *seg_x = iter->seg_x; artfloat *seg_dx = iter->seg_dx; int i = iter->seg_ix; int j; int x0 = iter->x0; int x1 = iter->x1; int y = iter->y; int seg_index; int x; ArtSVPRenderAAStep *steps = iter->steps; int n_steps; artfloat y_top, y_bot; artfloat x_top, x_bot; artfloat x_min, x_max; int ix_min, ix_max; artfloat delta; /* delta should be int too? */ int last, this; int xdelta; artfloat rslope, drslope; int start; const ArtSVPSeg *seg; int curs; artfloat dy; int sx; /* insert new active segments */ for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++) { if (svp->segs[i].bbox.y1 > y && svp->segs[i].bbox.x0 < x1) { seg = &svp->segs[i]; /* move cursor to topmost vector which overlaps [y,y+1) */ for (curs = 0; seg->points[curs + 1].y < y; curs++); cursor[i] = curs; dy = seg->points[curs + 1].y - seg->points[curs].y; if (fabs (dy) >= EPSILON) seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) / dy; else seg_dx[i] = 1e12; seg_x[i] = seg->points[curs].x + (y - seg->points[curs].y) * seg_dx[i]; art_svp_render_insert_active (i, active_segs, n_active_segs++, seg_x, seg_dx); } } n_steps = 0; /* render the runlengths, advancing and deleting as we go */ start = 0x8000; for (j = 0; j < n_active_segs; j++) { seg_index = active_segs[j]; seg = &svp->segs[seg_index]; curs = cursor[seg_index]; while (curs != seg->n_points - 1 && seg->points[curs].y < y + 1) { y_top = y; if (y_top < seg->points[curs].y) y_top = seg->points[curs].y; y_bot = y + 1; if (y_bot > seg->points[curs + 1].y) y_bot = seg->points[curs + 1].y; if (y_top != y_bot) { delta = (seg->dir ? 16711680.0 : -16711680.0) * (y_bot - y_top); x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index]; x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index]; if (x_top < x_bot) { x_min = x_top; x_max = x_bot; } else { x_min = x_bot; x_max = x_top; } ix_min = (int)floor (x_min); ix_max = (int)floor (x_max); if (ix_min >= x1) { /* skip; it starts to the right of the render region */ } else if (ix_max < x0) /* it ends to the left of the render region */ start += (int)delta; else if (ix_min == ix_max) { /* case 1, antialias a single pixel */ xdelta = (int)((ix_min + 1 - (x_min + x_max) * 0.5) * delta); ADD_STEP(ix_min, xdelta) if (ix_min + 1 < x1) { xdelta = (int)(delta - xdelta); ADD_STEP(ix_min + 1, xdelta) } } else { /* case 2, antialias a run */ rslope = 1.0 / fabs (seg_dx[seg_index]); drslope = delta * rslope; last = (int)(drslope * 0.5 * (ix_min + 1 - x_min) * (ix_min + 1 - x_min)); xdelta = last; if (ix_min >= x0) { ADD_STEP(ix_min, xdelta) x = ix_min + 1; } else { start += last; x = x0; } if (ix_max > x1) ix_max = x1; for (; x < ix_max; x++) { this = (int)((seg->dir ? 16711680.0 : -16711680.0) * rslope * (x + 0.5 - x_min)); xdelta = this - last; last = this; ADD_STEP(x, xdelta) } if (x < x1) { this = (int)(delta * (1 - 0.5 * (x_max - ix_max) * (x_max - ix_max) * rslope)); xdelta = this - last; last = this; ADD_STEP(x, xdelta) if (x + 1 < x1) { xdelta = (int)(delta - last); ADD_STEP(x + 1, xdelta) } } } } curs++; if (curs != seg->n_points - 1 && seg->points[curs].y < y + 1) { dy = seg->points[curs + 1].y - seg->points[curs].y; if (fabs (dy) >= EPSILON) seg_dx[seg_index] = (seg->points[curs + 1].x - seg->points[curs].x) / dy; else seg_dx[seg_index] = 1e12; seg_x[seg_index] = seg->points[curs].x + (y - seg->points[curs].y) * seg_dx[seg_index]; } /* break here, instead of duplicating predicate in while? */ } if (seg->points[curs].y >= y + 1) { curs--; cursor[seg_index] = curs; seg_x[seg_index] += seg_dx[seg_index]; } else { art_svp_render_delete_active (active_segs, j--, --n_active_segs); } } *p_start = start; *p_steps = steps; *p_n_steps = n_steps; iter->seg_ix = i; iter->n_active_segs = n_active_segs; iter->y++; } void art_svp_render_aa_iter_done (ArtSVPRenderAAIter *iter) { art_free (iter->steps); art_free (iter->seg_dx); art_free (iter->seg_x); art_free (iter->cursor); art_free (iter->active_segs); art_free (iter); } /** * art_svp_render_aa: Render SVP antialiased. * @svp: The #ArtSVP to render. * @x0: Left coordinate of destination rectangle. * @y0: Top coordinate of destination rectangle. * @x1: Right coordinate of destination rectangle. * @y1: Bottom coordinate of destination rectangle. * @callback: The callback which actually paints the pixels. * @callback_data: Private data for @callback. * * Renders the sorted vector path in the given rectangle, antialiased. * * This interface uses a callback for the actual pixel rendering. The * callback is called @y1 - @y0 times (once for each scan line). The y * coordinate is given as an argument for convenience (it could be * stored in the callback's private data and incremented on each * call). * * The rendered polygon is represented in a semi-runlength format: a * start value and a sequence of "steps". Each step has an x * coordinate and a value delta. The resulting value at position x is * equal to the sum of the start value and all step delta values for * which the step x coordinate is less than or equal to x. An * efficient algorithm will traverse the steps left to right, keeping * a running sum. * * All x coordinates in the steps are guaranteed to be @x0 <= x < @x1. * (This guarantee is a change from the gfonted vpaar renderer from * which this routine is derived, and is designed to simplify the * callback). * * The value 0x8000 represents 0% coverage by the polygon, while * 0xff8000 represents 100% coverage. This format is designed so that * >> 16 results in a standard 0x00..0xff value range, with nice * rounding. * **/ void art_svp_render_aa (const ArtSVP *svp, int x0, int y0, int x1, int y1, void (*callback) (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps), void *callback_data) { ArtSVPRenderAAIter *iter; int y; int start; ArtSVPRenderAAStep *steps; int n_steps; iter = art_svp_render_aa_iter (svp, x0, y0, x1, y1); for (y = y0; y < y1; y++) { art_svp_render_aa_iter_step (iter, &start, &steps, &n_steps); (*callback) (callback_data, y, start, steps, n_steps); } art_svp_render_aa_iter_done (iter); } rl-renderpm-4.0.3/src/libart_lgpl/art_svp_render_aa.h000066400000000000000000000036171453236046100226710ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_RENDER_AA_H__ #define __ART_SVP_RENDER_AA_H__ /* The spiffy antialiased renderer for sorted vector paths. */ #ifdef LIBART_COMPILATION #include "art_svp.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtSVPRenderAAStep ArtSVPRenderAAStep; typedef struct _ArtSVPRenderAAIter ArtSVPRenderAAIter; struct _ArtSVPRenderAAStep { int x; int delta; /* stored with 16 fractional bits */ }; ArtSVPRenderAAIter * art_svp_render_aa_iter (const ArtSVP *svp, int x0, int y0, int x1, int y1); void art_svp_render_aa_iter_step (ArtSVPRenderAAIter *iter, int *p_start, ArtSVPRenderAAStep **p_steps, int *p_n_steps); void art_svp_render_aa_iter_done (ArtSVPRenderAAIter *iter); void art_svp_render_aa (const ArtSVP *svp, int x0, int y0, int x1, int y1, void (*callback) (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps), void *callback_data); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_RENDER_AA_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_vpath.c000066400000000000000000000127071453236046100220660ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Sort vector paths into sorted vector paths */ #include "config.h" #include "art_svp_vpath.h" #include #include #include "art_misc.h" #include "art_vpath.h" #include "art_svp.h" /* reverse a list of points in place */ static void reverse_points (ArtPoint *points, int n_points) { int i; ArtPoint tmp_p; for (i = 0; i < (n_points >> 1); i++) { tmp_p = points[i]; points[i] = points[n_points - (i + 1)]; points[n_points - (i + 1)] = tmp_p; } } /** * art_svp_from_vpath: Convert a vpath to a sorted vector path. * @vpath: #ArtVPath to convert. * * Converts a vector path into sorted vector path form. The svp form is * more efficient for rendering and other vector operations. * * Basically, the implementation is to traverse the vector path, * generating a new segment for each "run" of points in the vector * path with monotonically increasing Y values. All the resulting * values are then sorted. * * Note: I'm not sure that the sorting rule is correct with respect * to numerical stability issues. * * Return value: Resulting sorted vector path. **/ ArtSVP * art_svp_from_vpath (ArtVpath *vpath) { int n_segs, n_segs_max; ArtSVP *svp; int dir; int new_dir; int i; ArtPoint *points; int n_points, n_points_max; double x, y; double x_min, x_max; n_segs = 0; n_segs_max = 16; svp = (ArtSVP *)art_alloc (sizeof(ArtSVP) + (n_segs_max - 1) * sizeof(ArtSVPSeg)); dir = 0; n_points = 0; n_points_max = 0; points = NULL; i = 0; x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant, but it makes gcc -Wall -ansi -pedantic happier */ x_min = x_max = 0; /* same */ while (vpath[i].code != ART_END) { if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN) { if (points != NULL && n_points >= 2) { if (n_segs == n_segs_max) { n_segs_max <<= 1; svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) + (n_segs_max - 1) * sizeof(ArtSVPSeg)); } svp->segs[n_segs].n_points = n_points; svp->segs[n_segs].dir = (dir > 0); if (dir < 0) reverse_points (points, n_points); svp->segs[n_segs].points = points; svp->segs[n_segs].bbox.x0 = x_min; svp->segs[n_segs].bbox.x1 = x_max; svp->segs[n_segs].bbox.y0 = points[0].y; svp->segs[n_segs].bbox.y1 = points[n_points - 1].y; n_segs++; points = NULL; } if (points == NULL) { n_points_max = 4; points = art_new (ArtPoint, n_points_max); } n_points = 1; points[0].x = x = vpath[i].x; points[0].y = y = vpath[i].y; x_min = x; x_max = x; dir = 0; } else /* must be LINETO */ { new_dir = (vpath[i].y > y || (vpath[i].y == y && vpath[i].x > x)) ? 1 : -1; if (dir && dir != new_dir) { /* new segment */ x = points[n_points - 1].x; y = points[n_points - 1].y; if (n_segs == n_segs_max) { n_segs_max <<= 1; svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) + (n_segs_max - 1) * sizeof(ArtSVPSeg)); } svp->segs[n_segs].n_points = n_points; svp->segs[n_segs].dir = (dir > 0); if (dir < 0) reverse_points (points, n_points); svp->segs[n_segs].points = points; svp->segs[n_segs].bbox.x0 = x_min; svp->segs[n_segs].bbox.x1 = x_max; svp->segs[n_segs].bbox.y0 = points[0].y; svp->segs[n_segs].bbox.y1 = points[n_points - 1].y; n_segs++; n_points = 1; n_points_max = 4; points = art_new (ArtPoint, n_points_max); points[0].x = x; points[0].y = y; x_min = x; x_max = x; } if (points != NULL) { if (n_points == n_points_max) art_expand (points, ArtPoint, n_points_max); points[n_points].x = x = vpath[i].x; points[n_points].y = y = vpath[i].y; if (x < x_min) x_min = x; else if (x > x_max) x_max = x; n_points++; } dir = new_dir; } i++; } if (points != NULL) { if (n_points >= 2) { if (n_segs == n_segs_max) { n_segs_max <<= 1; svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) + (n_segs_max - 1) * sizeof(ArtSVPSeg)); } svp->segs[n_segs].n_points = n_points; svp->segs[n_segs].dir = (dir > 0); if (dir < 0) reverse_points (points, n_points); svp->segs[n_segs].points = points; svp->segs[n_segs].bbox.x0 = x_min; svp->segs[n_segs].bbox.x1 = x_max; svp->segs[n_segs].bbox.y0 = points[0].y; svp->segs[n_segs].bbox.y1 = points[n_points - 1].y; n_segs++; } else art_free (points); } svp->n_segs = n_segs; qsort (&svp->segs, n_segs, sizeof (ArtSVPSeg), art_svp_seg_compare); return svp; } rl-renderpm-4.0.3/src/libart_lgpl/art_svp_vpath.h000066400000000000000000000024021453236046100220620ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_VPATH_H__ #define __ART_SVP_VPATH_H__ #ifdef LIBART_COMPILATION #include "art_svp.h" #include "art_vpath.h" #else #include #include #endif /* Sort vector paths into sorted vector paths. */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtSVP * art_svp_from_vpath (ArtVpath *vpath); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_VPATH_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_vpath_stroke.c000066400000000000000000000522331453236046100234530ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_svp_vpath_stroke.h" #include #include #include "art_misc.h" #include "art_vpath.h" #include "art_svp.h" #ifdef ART_USE_NEW_INTERSECTOR #include "art_svp_intersect.h" #else #include "art_svp_wind.h" #endif #include "art_svp_vpath.h" #define EPSILON 1e-6 #define EPSILON_2 1e-12 #define yes_OPTIMIZE_INNER /* Render an arc segment starting at (xc + x0, yc + y0) to (xc + x1, yc + y1), centered at (xc, yc), and with given radius. Both x0^2 + y0^2 and x1^2 + y1^2 should be equal to radius^2. A positive value of radius means curve to the left, negative means curve to the right. */ static void art_svp_vpath_stroke_arc (ArtVpath **p_vpath, int *pn, int *pn_max, double xc, double yc, double x0, double y0, double x1, double y1, double radius, double flatness) { double theta; double th_0, th_1; int n_pts; int i; double aradius; aradius = fabs (radius); theta = 2 * M_SQRT2 * sqrt (flatness / aradius); th_0 = atan2 (y0, x0); th_1 = atan2 (y1, x1); if (radius > 0) { /* curve to the left */ if (th_0 < th_1) th_0 += M_PI * 2; n_pts = (int)ceil ((th_0 - th_1) / theta); } else { /* curve to the right */ if (th_1 < th_0) th_1 += M_PI * 2; n_pts = (int)ceil ((th_1 - th_0) / theta); } #ifdef VERBOSE printf ("start %f %f; th_0 = %f, th_1 = %f, r = %f, theta = %f\n", x0, y0, th_0, th_1, radius, theta); #endif art_vpath_add_point (p_vpath, pn, pn_max, ART_LINETO, xc + x0, yc + y0); for (i = 1; i < n_pts; i++) { theta = th_0 + (th_1 - th_0) * i / n_pts; art_vpath_add_point (p_vpath, pn, pn_max, ART_LINETO, xc + cos (theta) * aradius, yc + sin (theta) * aradius); #ifdef VERBOSE printf ("mid %f %f\n", cos (theta) * radius, sin (theta) * radius); #endif } art_vpath_add_point (p_vpath, pn, pn_max, ART_LINETO, xc + x1, yc + y1); #ifdef VERBOSE printf ("end %f %f\n", x1, y1); #endif } /* Assume that forw and rev are at point i0. Bring them to i1, joining with the vector i1 - i2. This used to be true, but isn't now that the stroke_raw code is filtering out (near)zero length vectors: {It so happens that all invocations of this function maintain the precondition i1 = i0 + 1, so we could decrease the number of arguments by one. We haven't done that here, though.} forw is to the line's right and rev is to its left. Precondition: no zero-length vectors, otherwise a divide by zero will happen. */ static void render_seg (ArtVpath **p_forw, int *pn_forw, int *pn_forw_max, ArtVpath **p_rev, int *pn_rev, int *pn_rev_max, ArtVpath *vpath, int i0, int i1, int i2, ArtPathStrokeJoinType join, double line_width, double miter_limit, double flatness) { double dx0, dy0; double dx1, dy1; double dlx0, dly0; double dlx1, dly1; double dmx, dmy; double dmr2; double scale; double cross; #ifdef VERBOSE printf ("join style = %d\n", join); #endif /* The vectors of the lines from i0 to i1 and i1 to i2. */ dx0 = vpath[i1].x - vpath[i0].x; dy0 = vpath[i1].y - vpath[i0].y; dx1 = vpath[i2].x - vpath[i1].x; dy1 = vpath[i2].y - vpath[i1].y; /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise 90 degrees, and scaled to the length of line_width. */ scale = line_width / sqrt (dx0 * dx0 + dy0 * dy0); dlx0 = dy0 * scale; dly0 = -dx0 * scale; /* Set dl[xy]1 to the vector from i1 to i2, rotated counterclockwise 90 degrees, and scaled to the length of line_width. */ scale = line_width / sqrt (dx1 * dx1 + dy1 * dy1); dlx1 = dy1 * scale; dly1 = -dx1 * scale; #ifdef VERBOSE printf ("%% render_seg: (%g, %g) - (%g, %g) - (%g, %g)\n", vpath[i0].x, vpath[i0].y, vpath[i1].x, vpath[i1].y, vpath[i2].x, vpath[i2].y); printf ("%% render_seg: d[xy]0 = (%g, %g), dl[xy]0 = (%g, %g)\n", dx0, dy0, dlx0, dly0); printf ("%% render_seg: d[xy]1 = (%g, %g), dl[xy]1 = (%g, %g)\n", dx1, dy1, dlx1, dly1); #endif /* now, forw's last point is expected to be colinear along d[xy]0 to point i0 - dl[xy]0, and rev with i0 + dl[xy]0. */ /* positive for positive area (i.e. left turn) */ cross = dx1 * dy0 - dx0 * dy1; dmx = (dlx0 + dlx1) * 0.5; dmy = (dly0 + dly1) * 0.5; dmr2 = dmx * dmx + dmy * dmy; if (join == ART_PATH_STROKE_JOIN_MITER && dmr2 * miter_limit * miter_limit < line_width * line_width) join = ART_PATH_STROKE_JOIN_BEVEL; /* the case when dmr2 is zero or very small bothers me (i.e. near a 180 degree angle) ALEX: So, we avoid the optimization when dmr2 is very small. This should be safe since dmx/y is only used in optimization and in MITER case, and MITER should be converted to BEVEL when dmr2 is very small. */ if (dmr2 > EPSILON_2) { scale = line_width * line_width / dmr2; dmx *= scale; dmy *= scale; } if (cross * cross < EPSILON_2 && dx0 * dx1 + dy0 * dy1 >= 0) { /* going straight */ #ifdef VERBOSE printf ("%% render_seg: straight\n"); #endif art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0); art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0); } else if (cross > 0) { /* left turn, forw is outside and rev is inside */ #ifdef VERBOSE printf ("%% render_seg: left\n"); #endif if ( #ifdef NO_OPTIMIZE_INNER 0 && #endif (dmr2 > EPSILON_2) && /* check that i1 + dm[xy] is inside i0-i1 rectangle */ (dx0 + dmx) * dx0 + (dy0 + dmy) * dy0 > 0 && /* and that i1 + dm[xy] is inside i1-i2 rectangle */ ((dx1 - dmx) * dx1 + (dy1 - dmy) * dy1 > 0) #ifdef PEDANTIC_INNER && /* check that i1 + dl[xy]1 is inside i0-i1 rectangle */ (dx0 + dlx1) * dx0 + (dy0 + dly1) * dy0 > 0 && /* and that i1 + dl[xy]0 is inside i1-i2 rectangle */ ((dx1 - dlx0) * dx1 + (dy1 - dly0) * dy1 > 0) #endif ) { /* can safely add single intersection point */ art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy); } else { /* need to loop-de-loop the inside */ art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0); art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x, vpath[i1].y); art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1); } if (join == ART_PATH_STROKE_JOIN_BEVEL) { /* bevel */ art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0); art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1); } else if (join == ART_PATH_STROKE_JOIN_MITER) { art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy); } else if (join == ART_PATH_STROKE_JOIN_ROUND) art_svp_vpath_stroke_arc (p_forw, pn_forw, pn_forw_max, vpath[i1].x, vpath[i1].y, -dlx0, -dly0, -dlx1, -dly1, line_width, flatness); } else { /* right turn, rev is outside and forw is inside */ #ifdef VERBOSE printf ("%% render_seg: right\n"); #endif if ( #ifdef NO_OPTIMIZE_INNER 0 && #endif (dmr2 > EPSILON_2) && /* check that i1 - dm[xy] is inside i0-i1 rectangle */ (dx0 - dmx) * dx0 + (dy0 - dmy) * dy0 > 0 && /* and that i1 - dm[xy] is inside i1-i2 rectangle */ ((dx1 + dmx) * dx1 + (dy1 + dmy) * dy1 > 0) #ifdef PEDANTIC_INNER && /* check that i1 - dl[xy]1 is inside i0-i1 rectangle */ (dx0 - dlx1) * dx0 + (dy0 - dly1) * dy0 > 0 && /* and that i1 - dl[xy]0 is inside i1-i2 rectangle */ ((dx1 + dlx0) * dx1 + (dy1 + dly0) * dy1 > 0) #endif ) { /* can safely add single intersection point */ art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy); } else { /* need to loop-de-loop the inside */ art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0); art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x, vpath[i1].y); art_vpath_add_point (p_forw, pn_forw, pn_forw_max, ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1); } if (join == ART_PATH_STROKE_JOIN_BEVEL) { /* bevel */ art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0); art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1); } else if (join == ART_PATH_STROKE_JOIN_MITER) { art_vpath_add_point (p_rev, pn_rev, pn_rev_max, ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy); } else if (join == ART_PATH_STROKE_JOIN_ROUND) art_svp_vpath_stroke_arc (p_rev, pn_rev, pn_rev_max, vpath[i1].x, vpath[i1].y, dlx0, dly0, dlx1, dly1, -line_width, flatness); } } /* caps i1, under the assumption of a vector from i0 */ static void render_cap (ArtVpath **p_result, int *pn_result, int *pn_result_max, ArtVpath *vpath, int i0, int i1, ArtPathStrokeCapType cap, double line_width, double flatness) { double dx0, dy0; double dlx0, dly0; double scale; int n_pts; int i; dx0 = vpath[i1].x - vpath[i0].x; dy0 = vpath[i1].y - vpath[i0].y; /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise 90 degrees, and scaled to the length of line_width. */ scale = line_width / sqrt (dx0 * dx0 + dy0 * dy0); dlx0 = dy0 * scale; dly0 = -dx0 * scale; #ifdef VERBOSE printf ("cap style = %d\n", cap); #endif switch (cap) { case ART_PATH_STROKE_CAP_BUTT: art_vpath_add_point (p_result, pn_result, pn_result_max, ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0); art_vpath_add_point (p_result, pn_result, pn_result_max, ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0); break; case ART_PATH_STROKE_CAP_ROUND: n_pts = (int)ceil (M_PI / (2.0 * M_SQRT2 * sqrt (flatness / line_width))); art_vpath_add_point (p_result, pn_result, pn_result_max, ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0); for (i = 1; i < n_pts; i++) { double theta, c_th, s_th; theta = M_PI * i / n_pts; c_th = cos (theta); s_th = sin (theta); art_vpath_add_point (p_result, pn_result, pn_result_max, ART_LINETO, vpath[i1].x - dlx0 * c_th - dly0 * s_th, vpath[i1].y - dly0 * c_th + dlx0 * s_th); } art_vpath_add_point (p_result, pn_result, pn_result_max, ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0); break; case ART_PATH_STROKE_CAP_SQUARE: art_vpath_add_point (p_result, pn_result, pn_result_max, ART_LINETO, vpath[i1].x - dlx0 - dly0, vpath[i1].y - dly0 + dlx0); art_vpath_add_point (p_result, pn_result, pn_result_max, ART_LINETO, vpath[i1].x + dlx0 - dly0, vpath[i1].y + dly0 + dlx0); break; } } /** * art_svp_from_vpath_raw: Stroke a vector path, raw version * @vpath: #ArtVPath to stroke. * @join: Join style. * @cap: Cap style. * @line_width: Width of stroke. * @miter_limit: Miter limit. * @flatness: Flatness. * * Exactly the same as art_svp_vpath_stroke(), except that the resulting * stroke outline may self-intersect and have regions of winding number * greater than 1. * * Return value: Resulting raw stroked outline in svp format. **/ ArtVpath * art_svp_vpath_stroke_raw (ArtVpath *vpath, ArtPathStrokeJoinType join, ArtPathStrokeCapType cap, double line_width, double miter_limit, double flatness) { int begin_idx, end_idx; int i; ArtVpath *forw, *rev; int n_forw, n_rev; int n_forw_max, n_rev_max; ArtVpath *result; int n_result, n_result_max; double half_lw = 0.5 * line_width; int closed; int last, this, next, second; double dx, dy; n_forw_max = 16; forw = art_new (ArtVpath, n_forw_max); n_rev_max = 16; rev = art_new (ArtVpath, n_rev_max); n_result = 0; n_result_max = 16; result = art_new (ArtVpath, n_result_max); for (begin_idx = 0; vpath[begin_idx].code != ART_END; begin_idx = end_idx) { n_forw = 0; n_rev = 0; closed = (vpath[begin_idx].code == ART_MOVETO); /* we don't know what the first point joins with until we get to the last point and see if it's closed. So we start with the second line in the path. Note: this is not strictly true (we now know it's closed from the opening pathcode), but why fix code that isn't broken? */ this = begin_idx; /* skip over identical points at the beginning of the subpath */ for (i = this + 1; vpath[i].code == ART_LINETO; i++) { dx = vpath[i].x - vpath[this].x; dy = vpath[i].y - vpath[this].y; if (dx * dx + dy * dy > EPSILON_2) break; } next = i; second = next; /* invariant: this doesn't coincide with next */ while (vpath[next].code == ART_LINETO) { last = this; this = next; /* skip over identical points after the beginning of the subpath */ for (i = this + 1; vpath[i].code == ART_LINETO; i++) { dx = vpath[i].x - vpath[this].x; dy = vpath[i].y - vpath[this].y; if (dx * dx + dy * dy > EPSILON_2) break; } next = i; if (vpath[next].code != ART_LINETO) { /* reached end of path */ /* make "closed" detection conform to PostScript semantics (i.e. explicit closepath code rather than just the fact that end of the path is the beginning) */ if (closed && vpath[this].x == vpath[begin_idx].x && vpath[this].y == vpath[begin_idx].y) { int j; /* path is closed, render join to beginning */ render_seg (&forw, &n_forw, &n_forw_max, &rev, &n_rev, &n_rev_max, vpath, last, this, second, join, half_lw, miter_limit, flatness); #ifdef VERBOSE printf ("%% forw %d, rev %d\n", n_forw, n_rev); #endif /* do forward path */ art_vpath_add_point (&result, &n_result, &n_result_max, ART_MOVETO, forw[n_forw - 1].x, forw[n_forw - 1].y); for (j = 0; j < n_forw; j++) art_vpath_add_point (&result, &n_result, &n_result_max, ART_LINETO, forw[j].x, forw[j].y); /* do reverse path, reversed */ art_vpath_add_point (&result, &n_result, &n_result_max, ART_MOVETO, rev[0].x, rev[0].y); for (j = n_rev - 1; j >= 0; j--) art_vpath_add_point (&result, &n_result, &n_result_max, ART_LINETO, rev[j].x, rev[j].y); } else { /* path is open */ int j; /* add to forw rather than result to ensure that forw has at least one point. */ render_cap (&forw, &n_forw, &n_forw_max, vpath, last, this, cap, half_lw, flatness); art_vpath_add_point (&result, &n_result, &n_result_max, ART_MOVETO, forw[0].x, forw[0].y); for (j = 1; j < n_forw; j++) art_vpath_add_point (&result, &n_result, &n_result_max, ART_LINETO, forw[j].x, forw[j].y); for (j = n_rev - 1; j >= 0; j--) art_vpath_add_point (&result, &n_result, &n_result_max, ART_LINETO, rev[j].x, rev[j].y); render_cap (&result, &n_result, &n_result_max, vpath, second, begin_idx, cap, half_lw, flatness); art_vpath_add_point (&result, &n_result, &n_result_max, ART_LINETO, forw[0].x, forw[0].y); } } else render_seg (&forw, &n_forw, &n_forw_max, &rev, &n_rev, &n_rev_max, vpath, last, this, next, join, half_lw, miter_limit, flatness); } end_idx = next; } art_free (forw); art_free (rev); #ifdef VERBOSE printf ("%% n_result = %d\n", n_result); #endif art_vpath_add_point (&result, &n_result, &n_result_max, ART_END, 0, 0); return result; } #define noVERBOSE #ifdef VERBOSE #define XOFF 50 #define YOFF 700 static void print_ps_vpath (ArtVpath *vpath) { int i; for (i = 0; vpath[i].code != ART_END; i++) { switch (vpath[i].code) { case ART_MOVETO: printf ("%g %g moveto\n", XOFF + vpath[i].x, YOFF - vpath[i].y); break; case ART_LINETO: printf ("%g %g lineto\n", XOFF + vpath[i].x, YOFF - vpath[i].y); break; default: break; } } printf ("stroke showpage\n"); } static void print_ps_svp (ArtSVP *vpath) { int i, j; printf ("%% begin\n"); for (i = 0; i < vpath->n_segs; i++) { printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0); for (j = 0; j < vpath->segs[i].n_points; j++) { printf ("%g %g %s\n", XOFF + vpath->segs[i].points[j].x, YOFF - vpath->segs[i].points[j].y, j ? "lineto" : "moveto"); } printf ("stroke\n"); } printf ("showpage\n"); } #endif /* Render a vector path into a stroked outline. Status of this routine: Basic correctness: Only miter and bevel line joins are implemented, and only butt line caps. Otherwise, seems to be fine. Numerical stability: We cheat (adding random perturbation). Thus, it seems very likely that no numerical stability problems will be seen in practice. Speed: Should be pretty good. Precision: The perturbation fuzzes the coordinates slightly, but not enough to be visible. */ /** * art_svp_vpath_stroke: Stroke a vector path. * @vpath: #ArtVPath to stroke. * @join: Join style. * @cap: Cap style. * @line_width: Width of stroke. * @miter_limit: Miter limit. * @flatness: Flatness. * * Computes an svp representing the stroked outline of @vpath. The * width of the stroked line is @line_width. * * Lines are joined according to the @join rule. Possible values are * ART_PATH_STROKE_JOIN_MITER (for mitered joins), * ART_PATH_STROKE_JOIN_ROUND (for round joins), and * ART_PATH_STROKE_JOIN_BEVEL (for bevelled joins). The mitered join * is converted to a bevelled join if the miter would extend to a * distance of more than @miter_limit * @line_width from the actual * join point. * * If there are open subpaths, the ends of these subpaths are capped * according to the @cap rule. Possible values are * ART_PATH_STROKE_CAP_BUTT (squared cap, extends exactly to end * point), ART_PATH_STROKE_CAP_ROUND (rounded half-circle centered at * the end point), and ART_PATH_STROKE_CAP_SQUARE (squared cap, * extending half @line_width past the end point). * * The @flatness parameter controls the accuracy of the rendering. It * is most important for determining the number of points to use to * approximate circular arcs for round lines and joins. In general, the * resulting vector path will be within @flatness pixels of the "ideal" * path containing actual circular arcs. I reserve the right to use * the @flatness parameter to convert bevelled joins to miters for very * small turn angles, as this would reduce the number of points in the * resulting outline path. * * The resulting path is "clean" with respect to self-intersections, i.e. * the winding number is 0 or 1 at each point. * * Return value: Resulting stroked outline in svp format. **/ ArtSVP * art_svp_vpath_stroke (ArtVpath *vpath, ArtPathStrokeJoinType join, ArtPathStrokeCapType cap, double line_width, double miter_limit, double flatness) { #ifdef ART_USE_NEW_INTERSECTOR ArtVpath *vpath_stroke; ArtSVP *svp, *svp2; ArtSvpWriter *swr; vpath_stroke = art_svp_vpath_stroke_raw (vpath, join, cap, line_width, miter_limit, flatness); #ifdef VERBOSE print_ps_vpath (vpath_stroke); #endif svp = art_svp_from_vpath (vpath_stroke); #ifdef VERBOSE print_ps_svp (svp); #endif art_free (vpath_stroke); swr = art_svp_writer_rewind_new (ART_WIND_RULE_NONZERO); art_svp_intersector (svp, swr); svp2 = art_svp_writer_rewind_reap (swr); #ifdef VERBOSE print_ps_svp (svp2); #endif art_svp_free (svp); return svp2; #else ArtVpath *vpath_stroke, *vpath2; ArtSVP *svp, *svp2, *svp3; vpath_stroke = art_svp_vpath_stroke_raw (vpath, join, cap, line_width, miter_limit, flatness); #ifdef VERBOSE print_ps_vpath (vpath_stroke); #endif vpath2 = art_vpath_perturb (vpath_stroke); #ifdef VERBOSE print_ps_vpath (vpath2); #endif art_free (vpath_stroke); svp = art_svp_from_vpath (vpath2); #ifdef VERBOSE print_ps_svp (svp); #endif art_free (vpath2); svp2 = art_svp_uncross (svp); #ifdef VERBOSE print_ps_svp (svp2); #endif art_svp_free (svp); svp3 = art_svp_rewind_uncrossed (svp2, ART_WIND_RULE_NONZERO); #ifdef VERBOSE print_ps_svp (svp3); #endif art_svp_free (svp2); return svp3; #endif } rl-renderpm-4.0.3/src/libart_lgpl/art_svp_vpath_stroke.h000066400000000000000000000036471453236046100234650ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_VPATH_STROKE_H__ #define __ART_SVP_VPATH_STROKE_H__ /* Sort vector paths into sorted vector paths. */ #ifdef LIBART_COMPILATION #include "art_svp.h" #include "art_vpath.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef enum { ART_PATH_STROKE_JOIN_MITER, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_JOIN_BEVEL } ArtPathStrokeJoinType; typedef enum { ART_PATH_STROKE_CAP_BUTT, ART_PATH_STROKE_CAP_ROUND, ART_PATH_STROKE_CAP_SQUARE } ArtPathStrokeCapType; ArtSVP * art_svp_vpath_stroke (ArtVpath *vpath, ArtPathStrokeJoinType join, ArtPathStrokeCapType cap, double line_width, double miter_limit, double flatness); /* This version may have winding numbers exceeding 1. */ ArtVpath * art_svp_vpath_stroke_raw (ArtVpath *vpath, ArtPathStrokeJoinType join, ArtPathStrokeCapType cap, double line_width, double miter_limit, double flatness); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_VPATH_STROKE_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_svp_wind.c000066400000000000000000001177051453236046100217110ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Primitive intersection and winding number operations on sorted vector paths. These routines are internal to libart, used to construct operations like intersection, union, and difference. */ #include "config.h" #include "art_svp_wind.h" #include /* for fprintf of debugging info */ #include /* for memcpy */ #include #include "art_misc.h" #include "art_rect.h" #include "art_svp.h" #ifdef NOSTDERR # define STDERR stdout #else # define STDERR stderr #endif #define noVERBOSE #define PT_EQ(p1,p2) ((p1).x == (p2).x && (p1).y == (p2).y) #define PT_CLOSE(p1,p2) (fabs ((p1).x - (p2).x) < 1e-6 && fabs ((p1).y - (p2).y) < 1e-6) /* return nonzero and set *p to the intersection point if the lines z0-z1 and z2-z3 intersect each other. */ static int intersect_lines (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3, ArtPoint *p) { double a01, b01, c01; double a23, b23, c23; double d0, d1, d2, d3; double det; /* if the vectors share an endpoint, they don't intersect */ if (PT_EQ (z0, z2) || PT_EQ (z0, z3) || PT_EQ (z1, z2) || PT_EQ (z1, z3)) return 0; #if 0 if (PT_CLOSE (z0, z2) || PT_CLOSE (z0, z3) || PT_CLOSE (z1, z2) || PT_CLOSE (z1, z3)) return 0; #endif /* find line equations ax + by + c = 0 */ a01 = z0.y - z1.y; b01 = z1.x - z0.x; c01 = -(z0.x * a01 + z0.y * b01); /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y) = (z1.x * z0.y - z1.y * z0.x) */ d2 = a01 * z2.x + b01 * z2.y + c01; d3 = a01 * z3.x + b01 * z3.y + c01; if ((d2 > 0) == (d3 > 0)) return 0; a23 = z2.y - z3.y; b23 = z3.x - z2.x; c23 = -(z2.x * a23 + z2.y * b23); d0 = a23 * z0.x + b23 * z0.y + c23; d1 = a23 * z1.x + b23 * z1.y + c23; if ((d0 > 0) == (d1 > 0)) return 0; /* now we definitely know that the lines intersect */ /* solve the two linear equations ax + by + c = 0 */ det = 1.0 / (a01 * b23 - a23 * b01); p->x = det * (c23 * b01 - c01 * b23); p->y = det * (c01 * a23 - c23 * a01); return 1; } #define EPSILON 1e-6 static double trap_epsilon (double v) { const double epsilon = EPSILON; if (v < epsilon && v > -epsilon) return 0; else return v; } /* Determine the order of line segments z0-z1 and z2-z3. Return +1 if z2-z3 lies entirely to the right of z0-z1, -1 if entirely to the left, or 0 if overlap. The case analysis in this function is quite ugly. The fact that it's almost 200 lines long is ridiculous. Ok, so here's the plan to cut it down: First, do a bounding line comparison on the x coordinates. This is pretty much the common case, and should go quickly. It also takes care of the case where both lines are horizontal. Then, do d0 and d1 computation, but only if a23 is nonzero. Finally, do d2 and d3 computation, but only if a01 is nonzero. Fall through to returning 0 (this will happen when both lines are horizontal and they overlap). */ static int x_order (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3) { double a01, b01, c01; double a23, b23, c23; double d0, d1, d2, d3; if (z0.y == z1.y) { if (z2.y == z3.y) { double x01min, x01max; double x23min, x23max; if (z0.x > z1.x) { x01min = z1.x; x01max = z0.x; } else { x01min = z0.x; x01max = z1.x; } if (z2.x > z3.x) { x23min = z3.x; x23max = z2.x; } else { x23min = z2.x; x23max = z3.x; } if (x23min >= x01max) return 1; else if (x01min >= x23max) return -1; else return 0; } else { /* z0-z1 is horizontal, z2-z3 isn't */ a23 = z2.y - z3.y; b23 = z3.x - z2.x; c23 = -(z2.x * a23 + z2.y * b23); if (z3.y < z2.y) { a23 = -a23; b23 = -b23; c23 = -c23; } d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23); d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23); if (d0 > 0) { if (d1 >= 0) return 1; else return 0; } else if (d0 == 0) { if (d1 > 0) return 1; else if (d1 < 0) return -1; else fprintf (STDERR,"case 1 degenerate\n"); return 0; } else /* d0 < 0 */ { if (d1 <= 0) return -1; else return 0; } } } else if (z2.y == z3.y) { /* z2-z3 is horizontal, z0-z1 isn't */ a01 = z0.y - z1.y; b01 = z1.x - z0.x; c01 = -(z0.x * a01 + z0.y * b01); /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y) = (z1.x * z0.y - z1.y * z0.x) */ if (z1.y < z0.y) { a01 = -a01; b01 = -b01; c01 = -c01; } d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01); d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01); if (d2 > 0) { if (d3 >= 0) return -1; else return 0; } else if (d2 == 0) { if (d3 > 0) return -1; else if (d3 < 0) return 1; else fprintf (STDERR,"case 2 degenerate\n"); return 0; } else /* d2 < 0 */ { if (d3 <= 0) return 1; else return 0; } } /* find line equations ax + by + c = 0 */ a01 = z0.y - z1.y; b01 = z1.x - z0.x; c01 = -(z0.x * a01 + z0.y * b01); /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y) = -(z1.x * z0.y - z1.y * z0.x) */ if (a01 > 0) { a01 = -a01; b01 = -b01; c01 = -c01; } /* so now, (a01, b01) points to the left, thus a01 * x + b01 * y + c01 is negative if the point lies to the right of the line */ d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01); d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01); if (d2 > 0) { if (d3 >= 0) return -1; } else if (d2 == 0) { if (d3 > 0) return -1; else if (d3 < 0) return 1; else fprintf (STDERR, "colinear!\n"); } else /* d2 < 0 */ { if (d3 <= 0) return 1; } a23 = z2.y - z3.y; b23 = z3.x - z2.x; c23 = -(z2.x * a23 + z2.y * b23); if (a23 > 0) { a23 = -a23; b23 = -b23; c23 = -c23; } d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23); d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23); if (d0 > 0) { if (d1 >= 0) return 1; } else if (d0 == 0) { if (d1 > 0) return 1; else if (d1 < 0) return -1; else fprintf (STDERR, "colinear!\n"); } else /* d0 < 0 */ { if (d1 <= 0) return -1; } return 0; } /* similar to x_order, but to determine whether point z0 + epsilon lies to the left of the line z2-z3 or to the right */ static int x_order_2 (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3) { double a23, b23, c23; double d0, d1; a23 = z2.y - z3.y; b23 = z3.x - z2.x; c23 = -(z2.x * a23 + z2.y * b23); if (a23 > 0) { a23 = -a23; b23 = -b23; c23 = -c23; } d0 = a23 * z0.x + b23 * z0.y + c23; if (d0 > EPSILON) return -1; else if (d0 < -EPSILON) return 1; d1 = a23 * z1.x + b23 * z1.y + c23; if (d1 > EPSILON) return -1; else if (d1 < -EPSILON) return 1; if (z0.x == z1.x && z1.x == z2.x && z2.x == z3.x) { fprintf (STDERR, "x_order_2: colinear and horizontally aligned!\n"); return 0; } if (z0.x <= z2.x && z1.x <= z2.x && z0.x <= z3.x && z1.x <= z3.x) return -1; if (z0.x >= z2.x && z1.x >= z2.x && z0.x >= z3.x && z1.x >= z3.x) return 1; fprintf (STDERR, "x_order_2: colinear!\n"); return 0; } #ifdef DEAD_CODE /* Traverse the vector path, keeping it in x-sorted order. This routine doesn't actually do anything - it's just here for explanatory purposes. */ void traverse (ArtSVP *vp) { int *active_segs; int n_active_segs; int *cursor; int seg_idx; double y; int tmp1, tmp2; int asi; int i, j; active_segs = art_new (int, vp->n_segs); cursor = art_new (int, vp->n_segs); n_active_segs = 0; seg_idx = 0; y = vp->segs[0].points[0].y; while (seg_idx < vp->n_segs || n_active_segs > 0) { fprintf (STDERR,"y = %g\n", y); /* delete segments ending at y from active list */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; if (vp->segs[asi].n_points - 1 == cursor[asi] && vp->segs[asi].points[cursor[asi]].y == y) { fprintf (STDERR,"deleting %d\n", asi); n_active_segs--; for (j = i; j < n_active_segs; j++) active_segs[j] = active_segs[j + 1]; i--; } } /* insert new segments into the active list */ while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y) { cursor[seg_idx] = 0; fprintf (STDERR,"inserting %d\n", seg_idx); for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; if (x_order (vp->segs[asi].points[cursor[asi]], vp->segs[asi].points[cursor[asi] + 1], vp->segs[seg_idx].points[0], vp->segs[seg_idx].points[1]) == -1) break; } tmp1 = seg_idx; for (j = i; j < n_active_segs; j++) { tmp2 = active_segs[j]; active_segs[j] = tmp1; tmp1 = tmp2; } active_segs[n_active_segs] = tmp1; n_active_segs++; seg_idx++; } /* all active segs cross the y scanline (considering segs to be closed on top and open on bottom) */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; fprintf (STDERR,"%d (%g, %g) - (%g, %g) %s\n", asi, vp->segs[asi].points[cursor[asi]].x, vp->segs[asi].points[cursor[asi]].y, vp->segs[asi].points[cursor[asi] + 1].x, vp->segs[asi].points[cursor[asi] + 1].y, vp->segs[asi].dir ? "v" : "^"); } /* advance y to the next event */ if (n_active_segs == 0) { if (seg_idx < vp->n_segs) y = vp->segs[seg_idx].points[0].y; /* else we're done */ } else { asi = active_segs[0]; y = vp->segs[asi].points[cursor[asi] + 1].y; for (i = 1; i < n_active_segs; i++) { asi = active_segs[i]; if (y > vp->segs[asi].points[cursor[asi] + 1].y) y = vp->segs[asi].points[cursor[asi] + 1].y; } if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y) y = vp->segs[seg_idx].points[0].y; } /* advance cursors to reach new y */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; while (cursor[asi] < vp->segs[asi].n_points - 1 && y >= vp->segs[asi].points[cursor[asi] + 1].y) cursor[asi]++; } fprintf (STDERR,"\n"); } art_free (cursor); art_free (active_segs); } #endif /* I believe that the loop will always break with i=1. I think I'll want to change this from a simple sorted list to a modified stack. ips[*][0] will get its own data structure, and ips[*] will in general only be allocated if there is an intersection. Finally, the segment can be traced through the initial point (formerly ips[*][0]), backwards through the stack, and finally to cursor + 1. This change should cut down on allocation bandwidth, and also eliminate the iteration through n_ipl below. */ static void insert_ip (int seg_i, int *n_ips, int *n_ips_max, ArtPoint **ips, ArtPoint ip) { int i; ArtPoint tmp1, tmp2; int n_ipl; ArtPoint *ipl; n_ipl = n_ips[seg_i]++; if (n_ipl == n_ips_max[seg_i]) art_expand (ips[seg_i], ArtPoint, n_ips_max[seg_i]); ipl = ips[seg_i]; for (i = 1; i < n_ipl; i++) if (ipl[i].y > ip.y) break; tmp1 = ip; for (; i <= n_ipl; i++) { tmp2 = ipl[i]; ipl[i] = tmp1; tmp1 = tmp2; } } /* test active segment (i - 1) against i for intersection, if so, add intersection point to both ips lists. */ static void intersect_neighbors (int i, int *active_segs, int *n_ips, int *n_ips_max, ArtPoint **ips, int *cursor, ArtSVP *vp) { ArtPoint z0, z1, z2, z3; int asi01, asi23; ArtPoint ip; asi01 = active_segs[i - 1]; z0 = ips[asi01][0]; if (n_ips[asi01] == 1) z1 = vp->segs[asi01].points[cursor[asi01] + 1]; else z1 = ips[asi01][1]; asi23 = active_segs[i]; z2 = ips[asi23][0]; if (n_ips[asi23] == 1) z3 = vp->segs[asi23].points[cursor[asi23] + 1]; else z3 = ips[asi23][1]; if (intersect_lines (z0, z1, z2, z3, &ip)) { #ifdef VERBOSE fprintf (STDERR,"new intersection point: (%g, %g)\n", ip.x, ip.y); #endif insert_ip (asi01, n_ips, n_ips_max, ips, ip); insert_ip (asi23, n_ips, n_ips_max, ips, ip); } } /* Add a new point to a segment in the svp. Here, we also check to make sure that the segments satisfy nocross. However, this is only valuable for debugging, and could possibly be removed. */ static void svp_add_point (ArtSVP *svp, int *n_points_max, ArtPoint p, int *seg_map, int *active_segs, int n_active_segs, int i) { int asi, asi_left, asi_right; int n_points, n_points_left, n_points_right; ArtSVPSeg *seg; asi = seg_map[active_segs[i]]; seg = &svp->segs[asi]; n_points = seg->n_points; /* find out whether neighboring segments share a point */ if (i > 0) { asi_left = seg_map[active_segs[i - 1]]; n_points_left = svp->segs[asi_left].n_points; if (n_points_left > 1 && PT_EQ (svp->segs[asi_left].points[n_points_left - 2], svp->segs[asi].points[n_points - 1])) { /* ok, new vector shares a top point with segment to the left - now, check that it satisfies ordering invariant */ if (x_order (svp->segs[asi_left].points[n_points_left - 2], svp->segs[asi_left].points[n_points_left - 1], svp->segs[asi].points[n_points - 1], p) < 1) { #ifdef VERBOSE fprintf (STDERR,"svp_add_point: cross on left!\n"); #endif } } } if (i + 1 < n_active_segs) { asi_right = seg_map[active_segs[i + 1]]; n_points_right = svp->segs[asi_right].n_points; if (n_points_right > 1 && PT_EQ (svp->segs[asi_right].points[n_points_right - 2], svp->segs[asi].points[n_points - 1])) { /* ok, new vector shares a top point with segment to the right - now, check that it satisfies ordering invariant */ if (x_order (svp->segs[asi_right].points[n_points_right - 2], svp->segs[asi_right].points[n_points_right - 1], svp->segs[asi].points[n_points - 1], p) > -1) { #ifdef VERBOSE fprintf (STDERR,"svp_add_point: cross on right!\n"); #endif } } } if (n_points_max[asi] == n_points) art_expand (seg->points, ArtPoint, n_points_max[asi]); seg->points[n_points] = p; if (p.x < seg->bbox.x0) seg->bbox.x0 = p.x; else if (p.x > seg->bbox.x1) seg->bbox.x1 = p.x; seg->bbox.y1 = p.y; seg->n_points++; } #if 0 /* find where the segment (currently at i) is supposed to go, and return the target index - if equal to i, then there is no crossing problem. "Where it is supposed to go" is defined as following: Delete element i, re-insert at position target (bumping everything target and greater to the right). */ static int find_crossing (int i, int *active_segs, int n_active_segs, int *cursor, ArtPoint **ips, int *n_ips, ArtSVP *vp) { int asi, asi_left, asi_right; ArtPoint p0, p1; ArtPoint p0l, p1l; ArtPoint p0r, p1r; int target; asi = active_segs[i]; p0 = ips[asi][0]; if (n_ips[asi] == 1) p1 = vp->segs[asi].points[cursor[asi] + 1]; else p1 = ips[asi][1]; for (target = i; target > 0; target--) { asi_left = active_segs[target - 1]; p0l = ips[asi_left][0]; if (n_ips[asi_left] == 1) p1l = vp->segs[asi_left].points[cursor[asi_left] + 1]; else p1l = ips[asi_left][1]; if (!PT_EQ (p0, p0l)) break; #ifdef VERBOSE fprintf (STDERR,"point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n", p0l.x, p0l.y, p1l.x, p1l.y, p0.x, p0.y, p1.x, p1.y); #endif if (x_order (p0l, p1l, p0, p1) == 1) break; #ifdef VERBOSE fprintf (STDERR,"scanning to the left (i=%d, target=%d)\n", i, target); #endif } if (target < i) return target; for (; target < n_active_segs - 1; target++) { asi_right = active_segs[target + 1]; p0r = ips[asi_right][0]; if (n_ips[asi_right] == 1) p1r = vp->segs[asi_right].points[cursor[asi_right] + 1]; else p1r = ips[asi_right][1]; if (!PT_EQ (p0, p0r)) break; #ifdef VERBOSE fprintf (STDERR,"point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n", p0.x, p0.y, p1.x, p1.y, p0r.x, p0r.y, p1r.x, p1r.y); #endif if (x_order (p0r, p1r, p0, p1) == 1) break; #ifdef VERBOSE fprintf (STDERR,"scanning to the right (i=%d, target=%d)\n", i, target); #endif } return target; } #endif /* This routine handles the case where the segment changes its position in the active segment list. Generally, this will happen when the segment (defined by i and cursor) shares a top point with a neighbor, but breaks the ordering invariant. Essentially, this routine sorts the lines [start..end), all of which share a top point. This is implemented as your basic insertion sort. This routine takes care of intersecting the appropriate neighbors, as well. A first argument of -1 immediately returns, which helps reduce special casing in the main unwind routine. */ static void fix_crossing (int start, int end, int *active_segs, int n_active_segs, int *cursor, ArtPoint **ips, int *n_ips, int *n_ips_max, ArtSVP *vp, int *seg_map, ArtSVP **p_new_vp, int *pn_segs_max, int **pn_points_max) { int i, j; int target; int asi, asj; ArtPoint p0i, p1i; ArtPoint p0j, p1j; int swap = 0; #ifdef VERBOSE int k; #endif ArtPoint *pts; #ifdef VERBOSE fprintf (STDERR,"fix_crossing: [%d..%d)", start, end); for (k = 0; k < n_active_segs; k++) fprintf (STDERR," %d", active_segs[k]); fprintf (STDERR,"\n"); #endif if (start == -1) return; for (i = start + 1; i < end; i++) { asi = active_segs[i]; if (cursor[asi] < vp->segs[asi].n_points - 1) { p0i = ips[asi][0]; if (n_ips[asi] == 1) p1i = vp->segs[asi].points[cursor[asi] + 1]; else p1i = ips[asi][1]; for (j = i - 1; j >= start; j--) { asj = active_segs[j]; if (cursor[asj] < vp->segs[asj].n_points - 1) { p0j = ips[asj][0]; if (n_ips[asj] == 1) p1j = vp->segs[asj].points[cursor[asj] + 1]; else p1j = ips[asj][1]; /* we _hope_ p0i = p0j */ if (x_order_2 (p0j, p1j, p0i, p1i) == -1) break; } } target = j + 1; /* target is where active_seg[i] _should_ be in active_segs */ if (target != i) { swap = 1; #ifdef VERBOSE fprintf (STDERR,"fix_crossing: at %i should be %i\n", i, target); #endif /* let's close off all relevant segments */ for (j = i; j >= target; j--) { asi = active_segs[j]; /* First conjunct: this isn't the last point in the original segment. Second conjunct: this isn't the first point in the new segment (i.e. already broken). */ if (cursor[asi] < vp->segs[asi].n_points - 1 && (*p_new_vp)->segs[seg_map[asi]].n_points != 1) { int seg_num; /* so break here */ #ifdef VERBOSE fprintf (STDERR,"closing off %d\n", j); #endif pts = art_new (ArtPoint, 16); pts[0] = ips[asi][0]; seg_num = art_svp_add_segment (p_new_vp, pn_segs_max, pn_points_max, 1, vp->segs[asi].dir, pts, NULL); (*pn_points_max)[seg_num] = 16; seg_map[asi] = seg_num; } } /* now fix the ordering in active_segs */ asi = active_segs[i]; for (j = i; j > target; j--) active_segs[j] = active_segs[j - 1]; active_segs[j] = asi; } } } if (swap && start > 0) { int as_start; as_start = active_segs[start]; if (cursor[as_start] < vp->segs[as_start].n_points) { #ifdef VERBOSE fprintf (STDERR,"checking intersection of %d, %d\n", start - 1, start); #endif intersect_neighbors (start, active_segs, n_ips, n_ips_max, ips, cursor, vp); } } if (swap && end < n_active_segs) { int as_end; as_end = active_segs[end - 1]; if (cursor[as_end] < vp->segs[as_end].n_points) { #ifdef VERBOSE fprintf (STDERR,"checking intersection of %d, %d\n", end - 1, end); #endif intersect_neighbors (end, active_segs, n_ips, n_ips_max, ips, cursor, vp); } } if (swap) { #ifdef VERBOSE fprintf (STDERR,"fix_crossing return: [%d..%d)", start, end); for (k = 0; k < n_active_segs; k++) fprintf (STDERR," %d", active_segs[k]); fprintf (STDERR,"\n"); #endif } } /* Return a new sorted vector that covers the same area as the argument, but which satisfies the nocross invariant. Basically, this routine works by finding the intersection points, and cutting the segments at those points. Status of this routine: Basic correctness: Seems ok. Numerical stability: known problems in the case of points falling on lines, and colinear lines. For actual use, randomly perturbing the vertices is currently recommended. Speed: pretty good, although a more efficient priority queue, as well as bbox culling of potential intersections, are two optimizations that could help. Precision: pretty good, although the numerical stability problems make this routine unsuitable for precise calculations of differences. */ /* Here is a more detailed description of the algorithm. It follows roughly the structure of traverse (above), but is obviously quite a bit more complex. Here are a few important data structures: A new sorted vector path (new_svp). For each (active) segment in the original, a list of intersection points. Of course, the original being traversed. The following invariants hold (in addition to the invariants of the traverse procedure). The new sorted vector path lies entirely above the y scan line. The new sorted vector path keeps the nocross invariant. For each active segment, the y scan line crosses the line from the first to the second of the intersection points (where the second point is cursor + 1 if there is only one intersection point). The list of intersection points + the (cursor + 1) point is kept in nondecreasing y order. Of the active segments, none of the lines from first to second intersection point cross the 1st ip..2nd ip line of the left or right neighbor. (However, such a line may cross further intersection points of the neighbors, or segments past the immediate neighbors). Of the active segments, all lines from 1st ip..2nd ip are in strictly increasing x_order (this is very similar to the invariant of the traverse procedure, but is explicitly stated here in terms of ips). (this basically says that nocross holds on the active segments) The combination of the new sorted vector path, the path through all the intersection points to cursor + 1, and [cursor + 1, n_points) covers the same area as the argument. Another important data structure is mapping from original segment number to new segment number. The algorithm is perhaps best understood as advancing the cursors while maintaining these invariants. Here's roughly how it's done. When deleting segments from the active list, those segments are added to the new sorted vector path. In addition, the neighbors may intersect each other, so they are intersection tested (see below). When inserting new segments, they are intersection tested against their neighbors. The top point of the segment becomes the first intersection point. Advancing the cursor is just a bit different from the traverse routine, as the cursor may advance through the intersection points as well. Only when there is a single intersection point in the list does the cursor advance in the original segment. In either case, the new vector is intersection tested against both neighbors. It also causes the vector over which the cursor is advancing to be added to the new svp. Two steps need further clarification: Intersection testing: the 1st ip..2nd ip lines of the neighbors are tested to see if they cross (using intersect_lines). If so, then the intersection point is added to the ip list of both segments, maintaining the invariant that the list of intersection points is nondecreasing in y). Adding vector to new svp: if the new vector shares a top x coordinate with another vector, then it is checked to see whether it is in order. If not, then both segments are "broken," and then restarted. Note: in the case when both segments are in the same order, they may simply be swapped without breaking. For the time being, I'm going to put some of these operations into subroutines. If it turns out to be a performance problem, I could try to reorganize the traverse procedure so that each is only called once, and inline them. But if it's not a performance problem, I'll just keep it this way, because it will probably help to make the code clearer, and I believe this code could use all the clarity it can get. */ /** * art_svp_uncross: Resolve self-intersections of an svp. * @vp: The original svp. * * Finds all the intersections within @vp, and constructs a new svp * with new points added at these intersections. * * This routine needs to be redone from scratch with numerical robustness * in mind. I'm working on it. * * Return value: The new svp. **/ ArtSVP * art_svp_uncross (ArtSVP *vp) { int *active_segs; int n_active_segs; int *cursor; int seg_idx; double y; int tmp1, tmp2; int asi; int i, j; /* new data structures */ /* intersection points; invariant: *ips[i] is only allocated if i is active */ int *n_ips, *n_ips_max; ArtPoint **ips; /* new sorted vector path */ int n_segs_max, seg_num; ArtSVP *new_vp; int *n_points_max; /* mapping from argument to new segment numbers - again, only valid if active */ int *seg_map; double y_curs; ArtPoint p_curs; int first_share; double share_x; ArtPoint *pts; n_segs_max = 16; new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) + (n_segs_max - 1) * sizeof(ArtSVPSeg)); new_vp->n_segs = 0; if (vp->n_segs == 0) return new_vp; active_segs = art_new (int, vp->n_segs); cursor = art_new (int, vp->n_segs); seg_map = art_new (int, vp->n_segs); n_ips = art_new (int, vp->n_segs); n_ips_max = art_new (int, vp->n_segs); ips = art_new (ArtPoint *, vp->n_segs); n_points_max = art_new (int, n_segs_max); n_active_segs = 0; seg_idx = 0; y = vp->segs[0].points[0].y; while (seg_idx < vp->n_segs || n_active_segs > 0) { #ifdef VERBOSE fprintf (STDERR,"y = %g\n", y); #endif /* maybe move deletions to end of loop (to avoid so much special casing on the end of a segment)? */ /* delete segments ending at y from active list */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; if (vp->segs[asi].n_points - 1 == cursor[asi] && vp->segs[asi].points[cursor[asi]].y == y) { do { #ifdef VERBOSE fprintf (STDERR,"deleting %d\n", asi); #endif art_free (ips[asi]); n_active_segs--; for (j = i; j < n_active_segs; j++) active_segs[j] = active_segs[j + 1]; if (i < n_active_segs) asi = active_segs[i]; else break; } while (vp->segs[asi].n_points - 1 == cursor[asi] && vp->segs[asi].points[cursor[asi]].y == y); /* test intersection of neighbors */ if (i > 0 && i < n_active_segs) intersect_neighbors (i, active_segs, n_ips, n_ips_max, ips, cursor, vp); i--; } } /* insert new segments into the active list */ while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y) { #ifdef VERBOSE fprintf (STDERR,"inserting %d\n", seg_idx); #endif cursor[seg_idx] = 0; for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; if (x_order_2 (vp->segs[seg_idx].points[0], vp->segs[seg_idx].points[1], vp->segs[asi].points[cursor[asi]], vp->segs[asi].points[cursor[asi] + 1]) == -1) break; } /* Create and initialize the intersection points data structure */ n_ips[seg_idx] = 1; n_ips_max[seg_idx] = 2; ips[seg_idx] = art_new (ArtPoint, n_ips_max[seg_idx]); ips[seg_idx][0] = vp->segs[seg_idx].points[0]; /* Start a new segment in the new vector path */ pts = art_new (ArtPoint, 16); pts[0] = vp->segs[seg_idx].points[0]; seg_num = art_svp_add_segment (&new_vp, &n_segs_max, &n_points_max, 1, vp->segs[seg_idx].dir, pts, NULL); n_points_max[seg_num] = 16; seg_map[seg_idx] = seg_num; tmp1 = seg_idx; for (j = i; j < n_active_segs; j++) { tmp2 = active_segs[j]; active_segs[j] = tmp1; tmp1 = tmp2; } active_segs[n_active_segs] = tmp1; n_active_segs++; if (i > 0) intersect_neighbors (i, active_segs, n_ips, n_ips_max, ips, cursor, vp); if (i + 1 < n_active_segs) intersect_neighbors (i + 1, active_segs, n_ips, n_ips_max, ips, cursor, vp); seg_idx++; } /* all active segs cross the y scanline (considering segs to be closed on top and open on bottom) */ #ifdef VERBOSE for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; fprintf (STDERR,"%d ", asi); for (j = 0; j < n_ips[asi]; j++) fprintf (STDERR,"(%g, %g) - ", ips[asi][j].x, ips[asi][j].y); fprintf (STDERR,"(%g, %g) %s\n", vp->segs[asi].points[cursor[asi] + 1].x, vp->segs[asi].points[cursor[asi] + 1].y, vp->segs[asi].dir ? "v" : "^"); } #endif /* advance y to the next event Note: this is quadratic. We'd probably get decent constant factor speed improvement by caching the y_curs values. */ if (n_active_segs == 0) { if (seg_idx < vp->n_segs) y = vp->segs[seg_idx].points[0].y; /* else we're done */ } else { asi = active_segs[0]; if (n_ips[asi] == 1) y = vp->segs[asi].points[cursor[asi] + 1].y; else y = ips[asi][1].y; for (i = 1; i < n_active_segs; i++) { asi = active_segs[i]; if (n_ips[asi] == 1) y_curs = vp->segs[asi].points[cursor[asi] + 1].y; else y_curs = ips[asi][1].y; if (y > y_curs) y = y_curs; } if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y) y = vp->segs[seg_idx].points[0].y; } first_share = -1; share_x = 0; /* to avoid gcc warning, although share_x is never used when first_share is -1 */ /* advance cursors to reach new y */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; if (n_ips[asi] == 1) p_curs = vp->segs[asi].points[cursor[asi] + 1]; else p_curs = ips[asi][1]; if (p_curs.y == y) { svp_add_point (new_vp, n_points_max, p_curs, seg_map, active_segs, n_active_segs, i); n_ips[asi]--; for (j = 0; j < n_ips[asi]; j++) ips[asi][j] = ips[asi][j + 1]; if (n_ips[asi] == 0) { ips[asi][0] = p_curs; n_ips[asi] = 1; cursor[asi]++; } if (first_share < 0 || p_curs.x != share_x) { /* this is where crossings are detected, and if found, the active segments switched around. */ fix_crossing (first_share, i, active_segs, n_active_segs, cursor, ips, n_ips, n_ips_max, vp, seg_map, &new_vp, &n_segs_max, &n_points_max); first_share = i; share_x = p_curs.x; } if (cursor[asi] < vp->segs[asi].n_points - 1) { if (i > 0) intersect_neighbors (i, active_segs, n_ips, n_ips_max, ips, cursor, vp); if (i + 1 < n_active_segs) intersect_neighbors (i + 1, active_segs, n_ips, n_ips_max, ips, cursor, vp); } } else { /* not on a cursor point */ fix_crossing (first_share, i, active_segs, n_active_segs, cursor, ips, n_ips, n_ips_max, vp, seg_map, &new_vp, &n_segs_max, &n_points_max); first_share = -1; } } /* fix crossing on last shared group */ fix_crossing (first_share, i, active_segs, n_active_segs, cursor, ips, n_ips, n_ips_max, vp, seg_map, &new_vp, &n_segs_max, &n_points_max); #ifdef VERBOSE fprintf (STDERR,"\n"); #endif } /* not necessary to sort, new segments only get added at y, which increases monotonically */ #if 0 qsort (&new_vp->segs, new_vp->n_segs, sizeof (svp_seg), svp_seg_compare); { int k; for (k = 0; k < new_vp->n_segs - 1; k++) { fprintf (STDERR,"(%g, %g) - (%g, %g) %s (%g, %g) - (%g, %g)\n", new_vp->segs[k].points[0].x, new_vp->segs[k].points[0].y, new_vp->segs[k].points[1].x, new_vp->segs[k].points[1].y, svp_seg_compare (&new_vp->segs[k], &new_vp->segs[k + 1]) > 1 ? ">": "<", new_vp->segs[k + 1].points[0].x, new_vp->segs[k + 1].points[0].y, new_vp->segs[k + 1].points[1].x, new_vp->segs[k + 1].points[1].y); } } #endif art_free (n_points_max); art_free (seg_map); art_free (n_ips_max); art_free (n_ips); art_free (ips); art_free (cursor); art_free (active_segs); return new_vp; } #define noVERBOSE /* Rewind a svp satisfying the nocross invariant. The winding number of a segment is defined as the winding number of the points to the left while travelling in the direction of the segment. Therefore it preincrements and postdecrements as a scan line is traversed from left to right. Status of this routine: Basic correctness: Was ok in gfonted. However, this code does not yet compute bboxes for the resulting svp segs. Numerical stability: known problems in the case of horizontal segments in polygons with any complexity. For actual use, randomly perturbing the vertices is recommended. Speed: good. Precision: good, except that no attempt is made to remove "hair". Doing random perturbation just makes matters worse. */ /** * art_svp_rewind_uncrossed: Rewind an svp satisfying the nocross invariant. * @vp: The original svp. * @rule: The winding rule. * * Creates a new svp with winding number of 0 or 1 everywhere. The @rule * argument specifies a rule for how winding numbers in the original * @vp map to the winding numbers in the result. * * With @rule == ART_WIND_RULE_NONZERO, the resulting svp has a * winding number of 1 where @vp has a nonzero winding number. * * With @rule == ART_WIND_RULE_INTERSECT, the resulting svp has a * winding number of 1 where @vp has a winding number greater than * 1. It is useful for computing intersections. * * With @rule == ART_WIND_RULE_ODDEVEN, the resulting svp has a * winding number of 1 where @vp has an odd winding number. It is * useful for implementing the even-odd winding rule of the * PostScript imaging model. * * With @rule == ART_WIND_RULE_POSITIVE, the resulting svp has a * winding number of 1 where @vp has a positive winding number. It is * useful for implementing asymmetric difference. * * This routine needs to be redone from scratch with numerical robustness * in mind. I'm working on it. * * Return value: The new svp. **/ ArtSVP * art_svp_rewind_uncrossed (ArtSVP *vp, ArtWindRule rule) { int *active_segs; int n_active_segs; int *cursor; int seg_idx; double y; int tmp1, tmp2; int asi; int i, j; ArtSVP *new_vp; int n_segs_max; int *winding; int left_wind; int wind; int keep, invert; #ifdef VERBOSE print_svp (vp); #endif n_segs_max = 16; new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) + (n_segs_max - 1) * sizeof(ArtSVPSeg)); new_vp->n_segs = 0; if (vp->n_segs == 0) return new_vp; winding = art_new (int, vp->n_segs); active_segs = art_new (int, vp->n_segs); cursor = art_new (int, vp->n_segs); n_active_segs = 0; seg_idx = 0; y = vp->segs[0].points[0].y; while (seg_idx < vp->n_segs || n_active_segs > 0) { #ifdef VERBOSE fprintf (STDERR,"y = %g\n", y); #endif /* delete segments ending at y from active list */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; if (vp->segs[asi].n_points - 1 == cursor[asi] && vp->segs[asi].points[cursor[asi]].y == y) { #ifdef VERBOSE fprintf (STDERR,"deleting %d\n", asi); #endif n_active_segs--; for (j = i; j < n_active_segs; j++) active_segs[j] = active_segs[j + 1]; i--; } } /* insert new segments into the active list */ while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y) { #ifdef VERBOSE fprintf (STDERR,"inserting %d\n", seg_idx); #endif cursor[seg_idx] = 0; for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; if (x_order_2 (vp->segs[seg_idx].points[0], vp->segs[seg_idx].points[1], vp->segs[asi].points[cursor[asi]], vp->segs[asi].points[cursor[asi] + 1]) == -1) break; } /* Determine winding number for this segment */ if (i == 0) left_wind = 0; else if (vp->segs[active_segs[i - 1]].dir) left_wind = winding[active_segs[i - 1]]; else left_wind = winding[active_segs[i - 1]] - 1; if (vp->segs[seg_idx].dir) wind = left_wind + 1; else wind = left_wind; winding[seg_idx] = wind; switch (rule) { case ART_WIND_RULE_NONZERO: keep = (wind == 1 || wind == 0); invert = (wind == 0); break; case ART_WIND_RULE_INTERSECT: keep = (wind == 2); invert = 0; break; case ART_WIND_RULE_ODDEVEN: keep = 1; invert = !(wind & 1); break; case ART_WIND_RULE_POSITIVE: keep = (wind == 1); invert = 0; break; default: keep = 0; invert = 0; break; } if (keep) { ArtPoint *points, *new_points; int n_points; int new_dir; #ifdef VERBOSE fprintf (STDERR,"keeping segment %d\n", seg_idx); #endif n_points = vp->segs[seg_idx].n_points; points = vp->segs[seg_idx].points; new_points = art_new (ArtPoint, n_points); memcpy (new_points, points, n_points * sizeof (ArtPoint)); new_dir = vp->segs[seg_idx].dir ^ invert; art_svp_add_segment (&new_vp, &n_segs_max, NULL, n_points, new_dir, new_points, &vp->segs[seg_idx].bbox); } tmp1 = seg_idx; for (j = i; j < n_active_segs; j++) { tmp2 = active_segs[j]; active_segs[j] = tmp1; tmp1 = tmp2; } active_segs[n_active_segs] = tmp1; n_active_segs++; seg_idx++; } #ifdef VERBOSE /* all active segs cross the y scanline (considering segs to be closed on top and open on bottom) */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; fprintf (STDERR,"%d:%d (%g, %g) - (%g, %g) %s %d\n", asi, cursor[asi], vp->segs[asi].points[cursor[asi]].x, vp->segs[asi].points[cursor[asi]].y, vp->segs[asi].points[cursor[asi] + 1].x, vp->segs[asi].points[cursor[asi] + 1].y, vp->segs[asi].dir ? "v" : "^", winding[asi]); } #endif /* advance y to the next event */ if (n_active_segs == 0) { if (seg_idx < vp->n_segs) y = vp->segs[seg_idx].points[0].y; /* else we're done */ } else { asi = active_segs[0]; y = vp->segs[asi].points[cursor[asi] + 1].y; for (i = 1; i < n_active_segs; i++) { asi = active_segs[i]; if (y > vp->segs[asi].points[cursor[asi] + 1].y) y = vp->segs[asi].points[cursor[asi] + 1].y; } if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y) y = vp->segs[seg_idx].points[0].y; } /* advance cursors to reach new y */ for (i = 0; i < n_active_segs; i++) { asi = active_segs[i]; while (cursor[asi] < vp->segs[asi].n_points - 1 && y >= vp->segs[asi].points[cursor[asi] + 1].y) cursor[asi]++; } #ifdef VERBOSE fprintf (STDERR,"\n"); #endif } art_free (cursor); art_free (active_segs); art_free (winding); return new_vp; } rl-renderpm-4.0.3/src/libart_lgpl/art_svp_wind.h000066400000000000000000000027551453236046100217140ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_SVP_WIND_H__ #define __ART_SVP_WIND_H__ /* Primitive intersection and winding number operations on sorted vector paths. */ #ifdef LIBART_COMPILATION #include "art_svp.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef ART_WIND_RULE_DEFINED #define ART_WIND_RULE_DEFINED typedef enum { ART_WIND_RULE_NONZERO, ART_WIND_RULE_INTERSECT, ART_WIND_RULE_ODDEVEN, ART_WIND_RULE_POSITIVE } ArtWindRule; #endif ArtSVP * art_svp_uncross (ArtSVP *vp); ArtSVP * art_svp_rewind_uncrossed (ArtSVP *vp, ArtWindRule rule); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_SVP_WIND_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_uta.c000066400000000000000000000044621453236046100206440ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_uta.h" #include #include "art_misc.h" /** * art_uta_new: Allocate a new uta. * @x0: Left coordinate of uta. * @y0: Top coordinate of uta. * @x1: Right coordinate of uta. * @y1: Bottom coordinate of uta. * * Allocates a new microtile array. The arguments are in units of * tiles, not pixels. * * Returns: the newly allocated #ArtUta. **/ ArtUta * art_uta_new (int x0, int y0, int x1, int y1) { ArtUta *uta; uta = art_new (ArtUta, 1); uta->x0 = x0; uta->y0 = y0; uta->width = x1 - x0; uta->height = y1 - y0; uta->utiles = art_new (ArtUtaBbox, uta->width * uta->height); memset (uta->utiles, 0, uta->width * uta->height * sizeof(ArtUtaBbox)); return uta; } /** * art_uta_new_coords: Allocate a new uta, based on pixel coordinates. * @x0: Left coordinate of uta. * @y0: Top coordinate of uta. * @x1: Right coordinate of uta. * @y1: Bottom coordinate of uta. * * Allocates a new microtile array. The arguments are in pixels * * Returns: the newly allocated #ArtUta. **/ ArtUta * art_uta_new_coords (int x0, int y0, int x1, int y1) { return art_uta_new (x0 >> ART_UTILE_SHIFT, y0 >> ART_UTILE_SHIFT, 1 + (x1 >> ART_UTILE_SHIFT), 1 + (y1 >> ART_UTILE_SHIFT)); } /** * art_uta_free: Free a uta. * @uta: The uta to free. * * Frees the microtile array structure, including the actual microtile * data. **/ void art_uta_free (ArtUta *uta) { art_free (uta->utiles); art_free (uta); } /* User to Aardvark! */ rl-renderpm-4.0.3/src/libart_lgpl/art_uta.h000066400000000000000000000036061453236046100206500ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_UTA_H__ #define __ART_UTA_H__ /* Basic data structures and constructors for microtile arrays */ #ifdef LIBART_COMPILATION #include "art_misc.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef art_u32 ArtUtaBbox; typedef struct _ArtUta ArtUta; #define ART_UTA_BBOX_CONS(x0, y0, x1, y1) (((x0) << 24) | ((y0) << 16) | \ ((x1) << 8) | (y1)) #define ART_UTA_BBOX_X0(ub) ((ub) >> 24) #define ART_UTA_BBOX_Y0(ub) (((ub) >> 16) & 0xff) #define ART_UTA_BBOX_X1(ub) (((ub) >> 8) & 0xff) #define ART_UTA_BBOX_Y1(ub) ((ub) & 0xff) #define ART_UTILE_SHIFT 5 #define ART_UTILE_SIZE (1 << ART_UTILE_SHIFT) /* Coordinates are shifted right by ART_UTILE_SHIFT wrt the real coordinates. */ struct _ArtUta { int x0; int y0; int width; int height; ArtUtaBbox *utiles; }; ArtUta * art_uta_new (int x0, int y0, int x1, int y1); ArtUta * art_uta_new_coords (int x0, int y0, int x1, int y1); void art_uta_free (ArtUta *uta); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_UTA_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_uta_ops.c000066400000000000000000000065511453236046100215260ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_uta_ops.h" #include #include "art_misc.h" #include "art_uta.h" #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif /** * art_uta_union: Compute union of two uta's. * @uta1: One uta. * @uta2: The other uta. * * Computes the union of @uta1 and @uta2. The union is approximate, * but coverage is guaranteed over all pixels included in either of * the arguments, ie more pixels may be covered than the "exact" * union. * * Note: this routine is used in the Gnome Canvas to accumulate the * region that needs to be repainted. However, since it copies over * the entire uta (which might be largish) even when the update may be * small, it can be a performance bottleneck. There are two approaches * to this problem, both of which are probably worthwhile. First, the * generated uta's should always be limited to the visible window, * thus guaranteeing that uta's never become large. Second, there * should be a new, destructive union operation that only touches a * small part of the uta when the update is small. * * Return value: The new union uta. **/ ArtUta * art_uta_union (ArtUta *uta1, ArtUta *uta2) { ArtUta *uta; int x0, y0, x1, y1; int x, y; int ix, ix1, ix2; ArtUtaBbox bb, bb1, bb2; x0 = MIN(uta1->x0, uta2->x0); y0 = MIN(uta1->y0, uta2->y0); x1 = MAX(uta1->x0 + uta1->width, uta2->x0 + uta2->width); y1 = MAX(uta1->y0 + uta1->height, uta2->y0 + uta2->height); uta = art_uta_new (x0, y0, x1, y1); /* could move the first two if/else statements out of the loop */ ix = 0; for (y = y0; y < y1; y++) { ix1 = (y - uta1->y0) * uta1->width + x0 - uta1->x0; ix2 = (y - uta2->y0) * uta2->width + x0 - uta2->x0; for (x = x0; x < x1; x++) { if (x < uta1->x0 || y < uta1->y0 || x >= uta1->x0 + uta1->width || y >= uta1->y0 + uta1->height) bb1 = 0; else bb1 = uta1->utiles[ix1]; if (x < uta2->x0 || y < uta2->y0 || x >= uta2->x0 + uta2->width || y >= uta2->y0 + uta2->height) bb2 = 0; else bb2 = uta2->utiles[ix2]; if (bb1 == 0) bb = bb2; else if (bb2 == 0) bb = bb1; else bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb1), ART_UTA_BBOX_X0(bb2)), MIN(ART_UTA_BBOX_Y0(bb1), ART_UTA_BBOX_Y0(bb2)), MAX(ART_UTA_BBOX_X1(bb1), ART_UTA_BBOX_X1(bb2)), MAX(ART_UTA_BBOX_Y1(bb1), ART_UTA_BBOX_Y1(bb2))); uta->utiles[ix] = bb; ix++; ix1++; ix2++; } } return uta; } rl-renderpm-4.0.3/src/libart_lgpl/art_uta_ops.h000066400000000000000000000023011453236046100215200ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_UTA_OPS_H__ #define __ART_UTA_OPS_H__ /* Basic operations on microtile arrays */ #ifdef LIBART_COMPILATION #include "art_uta.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtUta * art_uta_union (ArtUta *uta1, ArtUta *uta2); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_UTA_OPS_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_uta_rect.c000066400000000000000000000066101453236046100216560ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_uta_rect.h" #include "art_misc.h" #include "art_uta.h" #include "art_rect.h" /** * art_uta_from_irect: Generate uta covering a rectangle. * @bbox: The source rectangle. * * Generates a uta exactly covering @bbox. Please do not call this * function with a @bbox with zero height or width. * * Return value: the new uta. **/ ArtUta * art_uta_from_irect (ArtIRect *bbox) { ArtUta *uta; ArtUtaBbox *utiles; ArtUtaBbox bb; int width, height; int x, y; int xf0, yf0, xf1, yf1; int ix; uta = art_new (ArtUta, 1); uta->x0 = bbox->x0 >> ART_UTILE_SHIFT; uta->y0 = bbox->y0 >> ART_UTILE_SHIFT; width = ((bbox->x1 + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT) - uta->x0; height = ((bbox->y1 + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT) - uta->y0; utiles = art_new (ArtUtaBbox, width * height); uta->width = width; uta->height = height; uta->utiles = utiles; xf0 = bbox->x0 & (ART_UTILE_SIZE - 1); yf0 = bbox->y0 & (ART_UTILE_SIZE - 1); xf1 = ((bbox->x1 - 1) & (ART_UTILE_SIZE - 1)) + 1; yf1 = ((bbox->y1 - 1) & (ART_UTILE_SIZE - 1)) + 1; if (height == 1) { if (width == 1) utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, xf1, yf1); else { utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, ART_UTILE_SIZE, yf1); bb = ART_UTA_BBOX_CONS (0, yf0, ART_UTILE_SIZE, yf1); for (x = 1; x < width - 1; x++) utiles[x] = bb; utiles[x] = ART_UTA_BBOX_CONS (0, yf0, xf1, yf1); } } else { if (width == 1) { utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, xf1, ART_UTILE_SIZE); bb = ART_UTA_BBOX_CONS (xf0, 0, xf1, ART_UTILE_SIZE); for (y = 1; y < height - 1; y++) utiles[y] = bb; utiles[y] = ART_UTA_BBOX_CONS (xf0, 0, xf1, yf1); } else { utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, ART_UTILE_SIZE, ART_UTILE_SIZE); bb = ART_UTA_BBOX_CONS (0, yf0, ART_UTILE_SIZE, ART_UTILE_SIZE); for (x = 1; x < width - 1; x++) utiles[x] = bb; utiles[x] = ART_UTA_BBOX_CONS (0, yf0, xf1, ART_UTILE_SIZE); ix = width; for (y = 1; y < height - 1; y++) { utiles[ix++] = ART_UTA_BBOX_CONS (xf0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE); bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE); for (x = 1; x < width - 1; x++) utiles[ix++] = bb; utiles[ix++] = ART_UTA_BBOX_CONS (0, 0, xf1, ART_UTILE_SIZE); } utiles[ix++] = ART_UTA_BBOX_CONS (xf0, 0, ART_UTILE_SIZE, yf1); bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, yf1); for (x = 1; x < width - 1; x++) utiles[ix++] = bb; utiles[ix++] = ART_UTA_BBOX_CONS (0, 0, xf1, yf1); } } return uta; } rl-renderpm-4.0.3/src/libart_lgpl/art_uta_rect.h000066400000000000000000000023111453236046100216550ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_UTA_RECT_H__ #define __ART_UTA_RECT_H__ #ifdef LIBART_COMPILATION #include "art_rect.h" #include "art_uta.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtUta * art_uta_from_irect (ArtIRect *bbox); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_UTA_RECT_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_uta_svp.c000066400000000000000000000031441453236046100215300ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* LGPL Copyright 1998 Raph Levien */ #include "config.h" #include "art_uta_svp.h" #include "art_misc.h" #include "art_vpath.h" #include "art_uta.h" #include "art_uta_vpath.h" #include "art_svp.h" #include "art_vpath_svp.h" /** * art_uta_from_svp: Generate uta covering an svp. * @svp: The source svp. * * Generates a uta covering @svp. The resulting uta is of course * approximate, ie it may cover more pixels than covered by @svp. * * Note: I will want to replace this with a more direct * implementation. But this gets the api in place. * * Return value: the new uta. **/ ArtUta * art_uta_from_svp (const ArtSVP *svp) { ArtVpath *vpath; ArtUta *uta; vpath = art_vpath_from_svp (svp); uta = art_uta_from_vpath (vpath); art_free (vpath); return uta; } rl-renderpm-4.0.3/src/libart_lgpl/art_uta_svp.h000066400000000000000000000024111453236046100215310ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_UTA_SVP_H__ #define __ART_UTA_SVP_H__ /* Basic data structures and constructors for microtile arrays */ #ifdef LIBART_COMPILATION #include "art_svp.h" #include "art_uta.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtUta * art_uta_from_svp (const ArtSVP *svp); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_UTA_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_uta_vpath.c000066400000000000000000000233051453236046100220430ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "art_uta_vpath.h" #include #include "art_misc.h" #include "art_vpath.h" #include "art_uta.h" #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /* MAX */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif /* MIN */ /** * art_uta_add_line: Add a line to the uta. * @uta: The uta to modify. * @x0: X coordinate of line start point. * @y0: Y coordinate of line start point. * @x1: X coordinate of line end point. * @y1: Y coordinate of line end point. * @rbuf: Buffer containing first difference of winding number. * @rbuf_rowstride: Rowstride of @rbuf. * * Add the line (@x0, @y0) - (@x1, @y1) to @uta, and also update the * winding number buffer used for rendering the interior. @rbuf * contains the first partial difference (in the X direction) of the * winding number, measured in grid cells. Thus, each time that a line * crosses a horizontal uta grid line, an entry of @rbuf is * incremented if @y1 > @y0, decremented otherwise. * * Note that edge handling is fairly delicate. Please rtfs for * details. **/ void art_uta_add_line (ArtUta *uta, double x0, double y0, double x1, double y1, int *rbuf, int rbuf_rowstride) { int xmin, ymin; double xmax, ymax; int xmaxf, ymaxf; int xmaxc, ymaxc; int xt0, yt0; int xt1, yt1; int xf0, yf0; int xf1, yf1; int ix, ix1; ArtUtaBbox bb; xmin = (int)floor (MIN(x0, x1)); xmax = MAX(x0, x1); xmaxf = (int)floor (xmax); xmaxc = (int)ceil (xmax); ymin = (int)floor (MIN(y0, y1)); ymax = MAX(y0, y1); ymaxf = (int)floor (ymax); ymaxc = (int)ceil (ymax); xt0 = (xmin >> ART_UTILE_SHIFT) - uta->x0; yt0 = (ymin >> ART_UTILE_SHIFT) - uta->y0; xt1 = (xmaxf >> ART_UTILE_SHIFT) - uta->x0; yt1 = (ymaxf >> ART_UTILE_SHIFT) - uta->y0; if (xt0 == xt1 && yt0 == yt1) { /* entirely inside a microtile, this is easy! */ xf0 = xmin & (ART_UTILE_SIZE - 1); yf0 = ymin & (ART_UTILE_SIZE - 1); xf1 = (xmaxf & (ART_UTILE_SIZE - 1)) + xmaxc - xmaxf; yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf; ix = yt0 * uta->width + xt0; bb = uta->utiles[ix]; if (bb == 0) bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1); else bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), (unsigned)xf0), MIN(ART_UTA_BBOX_Y0(bb), (unsigned)yf0), MAX(ART_UTA_BBOX_X1(bb), (unsigned)xf1), MAX(ART_UTA_BBOX_Y1(bb), (unsigned)yf1)); uta->utiles[ix] = bb; } else { double dx, dy; int sx, sy; dx = x1 - x0; dy = y1 - y0; sx = dx > 0 ? 1 : dx < 0 ? -1 : 0; sy = dy > 0 ? 1 : dy < 0 ? -1 : 0; if (ymin == ymaxf) { /* special case horizontal (dx/dy slope would be infinite) */ xf0 = xmin & (ART_UTILE_SIZE - 1); yf0 = ymin & (ART_UTILE_SIZE - 1); xf1 = (xmaxf & (ART_UTILE_SIZE - 1)) + xmaxc - xmaxf; yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf; ix = yt0 * uta->width + xt0; ix1 = yt0 * uta->width + xt1; while (ix != ix1) { bb = uta->utiles[ix]; if (bb == 0) bb = ART_UTA_BBOX_CONS(xf0, yf0, ART_UTILE_SIZE, yf1); else bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), (unsigned)xf0), MIN(ART_UTA_BBOX_Y0(bb), (unsigned)yf0), ART_UTILE_SIZE, MAX(ART_UTA_BBOX_Y1(bb), (unsigned)yf1)); uta->utiles[ix] = bb; xf0 = 0; ix++; } bb = uta->utiles[ix]; if (bb == 0) bb = ART_UTA_BBOX_CONS(0, yf0, xf1, yf1); else bb = ART_UTA_BBOX_CONS(0, MIN(ART_UTA_BBOX_Y0(bb), (unsigned)yf0), MAX(ART_UTA_BBOX_X1(bb), (unsigned)xf1), MAX(ART_UTA_BBOX_Y1(bb), (unsigned)yf1)); uta->utiles[ix] = bb; } else { /* Do a Bresenham-style traversal of the line */ double dx_dy; double x, y; double xn, yn; /* normalize coordinates to uta origin */ x0 -= uta->x0 << ART_UTILE_SHIFT; y0 -= uta->y0 << ART_UTILE_SHIFT; x1 -= uta->x0 << ART_UTILE_SHIFT; y1 -= uta->y0 << ART_UTILE_SHIFT; if (dy < 0) { double tmp; tmp = x0; x0 = x1; x1 = tmp; tmp = y0; y0 = y1; y1 = tmp; dx = -dx; sx = -sx; dy = -dy; /* we leave sy alone, because it would always be 1, and we need it for the rbuf stuff. */ } xt0 = ((int)floor (x0) >> ART_UTILE_SHIFT); xt1 = ((int)floor (x1) >> ART_UTILE_SHIFT); /* now [xy]0 is above [xy]1 */ ix = yt0 * uta->width + xt0; ix1 = yt1 * uta->width + xt1; #ifdef VERBOSE printf ("%% ix = %d,%d; ix1 = %d,%d\n", xt0, yt0, xt1, yt1); #endif dx_dy = dx / dy; x = x0; y = y0; while (ix != ix1) { int dix; /* figure out whether next crossing is horizontal or vertical */ #ifdef VERBOSE printf ("%% %d,%d\n", xt0, yt0); #endif yn = (yt0 + 1) << ART_UTILE_SHIFT; /* xn is the intercept with bottom edge of this tile. The following expression is careful to result in exactly x1 when yn = y1. */ xn = x1 + dx_dy * (yn - y1); if (xt0 != (int)floor (xn) >> ART_UTILE_SHIFT) { /* horizontal crossing */ xt0 += sx; dix = sx; if (dx > 0) { xn = xt0 << ART_UTILE_SHIFT; yn = y0 + (xn - x0) / dx_dy; xf0 = (int)floor (x) & (ART_UTILE_SIZE - 1); xf1 = ART_UTILE_SIZE; } else { xn = (xt0 + 1) << ART_UTILE_SHIFT; yn = y0 + (xn - x0) / dx_dy; xf0 = 0; xmaxc = (int)ceil (x); xf1 = xmaxc - ((xt0 + 1) << ART_UTILE_SHIFT); } ymaxf = (int)floor (yn); ymaxc = (int)ceil (yn); yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf; } else { /* vertical crossing */ dix = uta->width; xf0 = (int)floor (MIN(x, xn)) & (ART_UTILE_SIZE - 1); xmax = MAX(x, xn); xmaxc = (int)ceil (xmax); xf1 = xmaxc - (xt0 << ART_UTILE_SHIFT); yf1 = ART_UTILE_SIZE; if (rbuf != NULL) rbuf[yt0 * rbuf_rowstride + xt0] += sy; yt0++; } yf0 = (int)floor (y) & (ART_UTILE_SIZE - 1); bb = uta->utiles[ix]; if (bb == 0) bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1); else bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), (unsigned)xf0), MIN(ART_UTA_BBOX_Y0(bb), (unsigned)yf0), MAX(ART_UTA_BBOX_X1(bb), (unsigned)xf1), MAX(ART_UTA_BBOX_Y1(bb), (unsigned)yf1)); uta->utiles[ix] = bb; x = xn; y = yn; ix += dix; } xmax = MAX(x, x1); xmaxc = (int)ceil (xmax); ymaxc = (int)ceil (y1); xf0 = (int)floor (MIN(x1, x)) & (ART_UTILE_SIZE - 1); yf0 = (int)floor (y) & (ART_UTILE_SIZE - 1); xf1 = xmaxc - (xt0 << ART_UTILE_SHIFT); yf1 = ymaxc - (yt0 << ART_UTILE_SHIFT); bb = uta->utiles[ix]; if (bb == 0) bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1); else bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), (unsigned)xf0), MIN(ART_UTA_BBOX_Y0(bb), (unsigned)yf0), MAX(ART_UTA_BBOX_X1(bb), (unsigned)xf1), MAX(ART_UTA_BBOX_Y1(bb), (unsigned)yf1)); uta->utiles[ix] = bb; } } } /** * art_uta_from_vpath: Generate uta covering a vpath. * @vec: The source vpath. * * Generates a uta covering @vec. The resulting uta is of course * approximate, ie it may cover more pixels than covered by @vec. * * Return value: the new uta. **/ ArtUta * art_uta_from_vpath (const ArtVpath *vec) { ArtUta *uta; ArtIRect bbox; int *rbuf; int i; double x, y; int sum; int xt, yt; ArtUtaBbox *utiles; ArtUtaBbox bb; int width; int height; int ix; art_vpath_bbox_irect (vec, &bbox); uta = art_uta_new_coords (bbox.x0, bbox.y0, bbox.x1, bbox.y1); width = uta->width; height = uta->height; utiles = uta->utiles; rbuf = art_new (int, width * height); for (i = 0; i < width * height; i++) rbuf[i] = 0; x = 0; y = 0; for (i = 0; vec[i].code != ART_END; i++) { switch (vec[i].code) { case ART_MOVETO: x = vec[i].x; y = vec[i].y; break; case ART_LINETO: art_uta_add_line (uta, vec[i].x, vec[i].y, x, y, rbuf, width); x = vec[i].x; y = vec[i].y; break; default: /* this shouldn't happen */ art_free (rbuf); art_free (uta); return NULL; } } /* now add in the filling from rbuf */ ix = 0; for (yt = 0; yt < height; yt++) { sum = 0; for (xt = 0; xt < width; xt++) { sum += rbuf[ix]; /* Nonzero winding rule - others are possible, but hardly worth it. */ if (sum != 0) { bb = utiles[ix]; bb &= 0xffff0000; bb |= (ART_UTILE_SIZE << 8) | ART_UTILE_SIZE; utiles[ix] = bb; if (xt != width - 1) { bb = utiles[ix + 1]; bb &= 0xffff00; bb |= ART_UTILE_SIZE; utiles[ix + 1] = bb; } if (yt != height - 1) { bb = utiles[ix + width]; bb &= 0xff0000ff; bb |= ART_UTILE_SIZE << 8; utiles[ix + width] = bb; if (xt != width - 1) { utiles[ix + width + 1] &= 0xffff; } } } ix++; } } art_free (rbuf); return uta; } rl-renderpm-4.0.3/src/libart_lgpl/art_uta_vpath.h000066400000000000000000000026561453236046100220560ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_UTA_VPATH_H__ #define __ART_UTA_VPATH_H__ /* Basic data structures and constructors for microtile arrays */ #ifdef LIBART_COMPILATION #include "art_uta.h" #include "art_vpath.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtUta * art_uta_from_vpath (const ArtVpath *vec); /* This is a private function: */ void art_uta_add_line (ArtUta *uta, double x0, double y0, double x1, double y1, int *rbuf, int rbuf_rowstride); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_UTA_VPATH_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_vpath.c000066400000000000000000000140421453236046100211700ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Basic constructors and operations for vector paths */ #include "config.h" #include "art_vpath.h" #include #include #include "art_misc.h" #include "art_rect.h" /** * art_vpath_add_point: Add point to vpath. * @p_vpath: Where the pointer to the #ArtVpath structure is stored. * @pn_points: Pointer to the number of points in *@p_vpath. * @pn_points_max: Pointer to the number of points allocated. * @code: The pathcode for the new point. * @x: The X coordinate of the new point. * @y: The Y coordinate of the new point. * * Adds a new point to *@p_vpath, reallocating and updating *@p_vpath * and *@pn_points_max as necessary. *@pn_points is incremented. * * This routine always adds the point after all points already in the * vpath. Thus, it should be called in the order the points are * desired. **/ void art_vpath_add_point (ArtVpath **p_vpath, int *pn_points, int *pn_points_max, ArtPathcode code, double x, double y) { int i; i = (*pn_points)++; if (i == *pn_points_max) art_expand (*p_vpath, ArtVpath, *pn_points_max); (*p_vpath)[i].code = code; (*p_vpath)[i].x = x; (*p_vpath)[i].y = y; } /* number of steps should really depend on radius. */ #define CIRCLE_STEPS 128 /** * art_vpath_new_circle: Create a new circle. * @x: X coordinate of center. * @y: Y coordinate of center. * @r: radius. * * Creates a new polygon closely approximating a circle with center * (@x, @y) and radius @r. Currently, the number of points used in the * approximation is fixed, but that will probably change. * * Return value: The newly created #ArtVpath. **/ ArtVpath * art_vpath_new_circle (double x, double y, double r) { int i; ArtVpath *vec; double theta; vec = art_new (ArtVpath, CIRCLE_STEPS + 2); for (i = 0; i < CIRCLE_STEPS + 1; i++) { vec[i].code = i ? ART_LINETO : ART_MOVETO; theta = (i & (CIRCLE_STEPS - 1)) * (M_PI * 2.0 / CIRCLE_STEPS); vec[i].x = x + r * cos (theta); vec[i].y = y - r * sin (theta); } vec[i].code = ART_END; return vec; } /** * art_vpath_affine_transform: Affine transform a vpath. * @src: Source vpath to transform. * @matrix: Affine transform. * * Computes the affine transform of the vpath, using @matrix as the * transform. @matrix is stored in the same format as PostScript, ie. * x' = @matrix[0] * x + @matrix[2] * y + @matrix[4] * y' = @matrix[1] * x + @matrix[3] * y + @matrix[5] * * Return value: the newly allocated vpath resulting from the transform. **/ ArtVpath * art_vpath_affine_transform (const ArtVpath *src, const double matrix[6]) { int i; int size; ArtVpath *new; double x, y; for (i = 0; src[i].code != ART_END; i++); size = i; new = art_new (ArtVpath, size + 1); for (i = 0; i < size; i++) { new[i].code = src[i].code; x = src[i].x; y = src[i].y; new[i].x = matrix[0] * x + matrix[2] * y + matrix[4]; new[i].y = matrix[1] * x + matrix[3] * y + matrix[5]; } new[i].code = ART_END; return new; } /** * art_vpath_bbox_drect: Determine bounding box of vpath. * @vec: Source vpath. * @drect: Where to store bounding box. * * Determines bounding box of @vec, and stores it in @drect. **/ void art_vpath_bbox_drect (const ArtVpath *vec, ArtDRect *drect) { int i; double x0, y0, x1, y1; if (vec[0].code == ART_END) { x0 = y0 = x1 = y1 = 0; } else { x0 = x1 = vec[0].x; y0 = y1 = vec[0].y; for (i = 1; vec[i].code != ART_END; i++) { if (vec[i].x < x0) x0 = vec[i].x; if (vec[i].x > x1) x1 = vec[i].x; if (vec[i].y < y0) y0 = vec[i].y; if (vec[i].y > y1) y1 = vec[i].y; } } drect->x0 = x0; drect->y0 = y0; drect->x1 = x1; drect->y1 = y1; } /** * art_vpath_bbox_irect: Determine integer bounding box of vpath. * @vec: Source vpath. * idrect: Where to store bounding box. * * Determines integer bounding box of @vec, and stores it in @irect. **/ void art_vpath_bbox_irect (const ArtVpath *vec, ArtIRect *irect) { ArtDRect drect; art_vpath_bbox_drect (vec, &drect); art_drect_to_irect (irect, &drect); } #define PERTURBATION 2e-3 /** * art_vpath_perturb: Perturb each point in vpath by small random amount. * @src: Source vpath. * * Perturbs each of the points by a small random amount. This is * helpful for cheating in cases when algorithms haven't attained * numerical stability yet. * * Return value: Newly allocated vpath containing perturbed @src. **/ ArtVpath * art_vpath_perturb (ArtVpath *src) { int i; int size; ArtVpath *new; double x, y; double x_start, y_start; int open; for (i = 0; src[i].code != ART_END; i++); size = i; new = art_new (ArtVpath, size + 1); x_start = 0; y_start = 0; open = 0; for (i = 0; i < size; i++) { new[i].code = src[i].code; x = src[i].x + (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5; y = src[i].y + (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5; if (src[i].code == ART_MOVETO) { x_start = x; y_start = y; open = 0; } else if (src[i].code == ART_MOVETO_OPEN) open = 1; if (!open && (i + 1 == size || src[i + 1].code != ART_LINETO)) { x = x_start; y = y_start; } new[i].x = x; new[i].y = y; } new[i].code = ART_END; return new; } rl-renderpm-4.0.3/src/libart_lgpl/art_vpath.h000066400000000000000000000035621453236046100212020ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_VPATH_H__ #define __ART_VPATH_H__ #ifdef LIBART_COMPILATION #include "art_rect.h" #include "art_pathcode.h" #else #include #include #endif /* Basic data structures and constructors for simple vector paths */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtVpath ArtVpath; /* CURVETO is not allowed! */ struct _ArtVpath { ArtPathcode code; double x; double y; }; /* Some of the functions need to go into their own modules */ void art_vpath_add_point (ArtVpath **p_vpath, int *pn_points, int *pn_points_max, ArtPathcode code, double x, double y); ArtVpath * art_vpath_new_circle (double x, double y, double r); ArtVpath * art_vpath_affine_transform (const ArtVpath *src, const double matrix[6]); void art_vpath_bbox_drect (const ArtVpath *vec, ArtDRect *drect); void art_vpath_bbox_irect (const ArtVpath *vec, ArtIRect *irect); ArtVpath * art_vpath_perturb (ArtVpath *src); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_VPATH_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_vpath_bpath.c000066400000000000000000000226211453236046100223500ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Basic constructors and operations for bezier paths */ #include "config.h" #include "art_vpath_bpath.h" #include #include "art_misc.h" #include "art_bpath.h" #include "art_vpath.h" /* p must be allocated 2^level points. */ /* level must be >= 1 */ ArtPoint * art_bezier_to_vec (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, ArtPoint *p, int level) { double x_m, y_m; #ifdef VERBOSE printf ("bezier_to_vec: %g,%g %g,%g %g,%g %g,%g %d\n", x0, y0, x1, y1, x2, y2, x3, y3, level); #endif if (level == 1) { x_m = (x0 + 3 * (x1 + x2) + x3) * 0.125; y_m = (y0 + 3 * (y1 + y2) + y3) * 0.125; p->x = x_m; p->y = y_m; p++; p->x = x3; p->y = y3; p++; #ifdef VERBOSE printf ("-> (%g, %g) -> (%g, %g)\n", x_m, y_m, x3, y3); #endif } else { double xa1, ya1; double xa2, ya2; double xb1, yb1; double xb2, yb2; xa1 = (x0 + x1) * 0.5; ya1 = (y0 + y1) * 0.5; xa2 = (x0 + 2 * x1 + x2) * 0.25; ya2 = (y0 + 2 * y1 + y2) * 0.25; xb1 = (x1 + 2 * x2 + x3) * 0.25; yb1 = (y1 + 2 * y2 + y3) * 0.25; xb2 = (x2 + x3) * 0.5; yb2 = (y2 + y3) * 0.5; x_m = (xa2 + xb1) * 0.5; y_m = (ya2 + yb1) * 0.5; #ifdef VERBOSE printf ("%g,%g %g,%g %g,%g %g,%g\n", xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2); #endif p = art_bezier_to_vec (x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, p, level - 1); p = art_bezier_to_vec (x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, p, level - 1); } return p; } #define RENDER_LEVEL 4 #define RENDER_SIZE (1 << (RENDER_LEVEL)) /** * art_vpath_render_bez: Render a bezier segment into the vpath. * @p_vpath: Where the pointer to the #ArtVpath structure is stored. * @pn_points: Pointer to the number of points in *@p_vpath. * @pn_points_max: Pointer to the number of points allocated. * @x0: X coordinate of starting bezier point. * @y0: Y coordinate of starting bezier point. * @x1: X coordinate of first bezier control point. * @y1: Y coordinate of first bezier control point. * @x2: X coordinate of second bezier control point. * @y2: Y coordinate of second bezier control point. * @x3: X coordinate of ending bezier point. * @y3: Y coordinate of ending bezier point. * @flatness: Flatness control. * * Renders a bezier segment into the vector path, reallocating and * updating *@p_vpath and *@pn_vpath_max as necessary. *@pn_vpath is * incremented by the number of vector points added. * * This step includes (@x0, @y0) but not (@x3, @y3). * * The @flatness argument guides the amount of subdivision. The Adobe * PostScript reference manual defines flatness as the maximum * deviation between the any point on the vpath approximation and the * corresponding point on the "true" curve, and we follow this * definition here. A value of 0.25 should ensure high quality for aa * rendering. **/ static void art_vpath_render_bez (ArtVpath **p_vpath, int *pn, int *pn_max, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double flatness) { double x3_0, y3_0; double z3_0_dot; double z1_dot, z2_dot; double z1_perp, z2_perp; double max_perp_sq; double x_m, y_m; double xa1, ya1; double xa2, ya2; double xb1, yb1; double xb2, yb2; /* It's possible to optimize this routine a fair amount. First, once the _dot conditions are met, they will also be met in all further subdivisions. So we might recurse to a different routine that only checks the _perp conditions. Second, the distance _should_ decrease according to fairly predictable rules (a factor of 4 with each subdivision). So it might be possible to note that the distance is within a factor of 4 of acceptable, and subdivide once. But proving this might be hard. Third, at the last subdivision, x_m and y_m can be computed more expeditiously (as in the routine above). Finally, if we were able to subdivide by, say 2 or 3, this would allow considerably finer-grain control, i.e. fewer points for the same flatness tolerance. This would speed things up downstream. In any case, this routine is unlikely to be the bottleneck. It's just that I have this undying quest for more speed... */ x3_0 = x3 - x0; y3_0 = y3 - y0; /* z3_0_dot is dist z0-z3 squared */ z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0; if (z3_0_dot < 0.001) { /* if start and end point are almost identical, the flatness tests * don't work properly, so fall back on testing whether both of * the other two control points are the same as the start point, * too. */ if (hypot(x1 - x0, y1 - y0) < 0.001 && hypot(x2 - x0, y2 - y0) < 0.001) goto nosubdivide; else goto subdivide; } /* we can avoid subdivision if: z1 has distance no more than flatness from the z0-z3 line z1 is no more z0'ward than flatness past z0-z3 z1 is more z0'ward than z3'ward on the line traversing z0-z3 and correspondingly for z2 */ /* perp is distance from line, multiplied by dist z0-z3 */ max_perp_sq = flatness * flatness * z3_0_dot; z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0; if (z1_perp * z1_perp > max_perp_sq) goto subdivide; z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0; if (z2_perp * z2_perp > max_perp_sq) goto subdivide; z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0; if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq) goto subdivide; z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0; if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq) goto subdivide; if (z1_dot + z1_dot > z3_0_dot) goto subdivide; if (z2_dot + z2_dot > z3_0_dot) goto subdivide; nosubdivide: /* don't subdivide */ art_vpath_add_point (p_vpath, pn, pn_max, ART_LINETO, x3, y3); return; subdivide: xa1 = (x0 + x1) * 0.5; ya1 = (y0 + y1) * 0.5; xa2 = (x0 + 2 * x1 + x2) * 0.25; ya2 = (y0 + 2 * y1 + y2) * 0.25; xb1 = (x1 + 2 * x2 + x3) * 0.25; yb1 = (y1 + 2 * y2 + y3) * 0.25; xb2 = (x2 + x3) * 0.5; yb2 = (y2 + y3) * 0.5; x_m = (xa2 + xb1) * 0.5; y_m = (ya2 + yb1) * 0.5; #ifdef VERBOSE printf ("%g,%g %g,%g %g,%g %g,%g\n", xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2); #endif art_vpath_render_bez (p_vpath, pn, pn_max, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness); art_vpath_render_bez (p_vpath, pn, pn_max, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness); } /** * art_bez_path_to_vec: Create vpath from bezier path. * @bez: Bezier path. * @flatness: Flatness control. * * Creates a vector path closely approximating the bezier path defined by * @bez. The @flatness argument controls the amount of subdivision. In * general, the resulting vpath deviates by at most @flatness pixels * from the "ideal" path described by @bez. * * Return value: Newly allocated vpath. **/ ArtVpath * art_bez_path_to_vec (const ArtBpath *bez, double flatness) { ArtVpath *vec; int vec_n, vec_n_max; int bez_index; double x, y; vec_n = 0; vec_n_max = RENDER_SIZE; vec = art_new (ArtVpath, vec_n_max); /* Initialization is unnecessary because of the precondition that the bezier path does not begin with LINETO or CURVETO, but is here to make the code warning-free. */ x = 0; y = 0; bez_index = 0; do { #ifdef VERBOSE printf ("%s %g %g\n", bez[bez_index].code == ART_CURVETO ? "curveto" : bez[bez_index].code == ART_LINETO ? "lineto" : bez[bez_index].code == ART_MOVETO ? "moveto" : bez[bez_index].code == ART_MOVETO_OPEN ? "moveto-open" : "end", bez[bez_index].x3, bez[bez_index].y3); #endif /* make sure space for at least one more code */ if (vec_n >= vec_n_max) art_expand (vec, ArtVpath, vec_n_max); switch (bez[bez_index].code) { case ART_MOVETO_OPEN: case ART_MOVETO: case ART_LINETO: x = bez[bez_index].x3; y = bez[bez_index].y3; vec[vec_n].code = bez[bez_index].code; vec[vec_n].x = x; vec[vec_n].y = y; vec_n++; break; case ART_END: vec[vec_n].code = bez[bez_index].code; vec[vec_n].x = 0; vec[vec_n].y = 0; vec_n++; break; case ART_CURVETO: #ifdef VERBOSE printf ("%g,%g %g,%g %g,%g %g,%g\n", x, y, bez[bez_index].x1, bez[bez_index].y1, bez[bez_index].x2, bez[bez_index].y2, bez[bez_index].x3, bez[bez_index].y3); #endif art_vpath_render_bez (&vec, &vec_n, &vec_n_max, x, y, bez[bez_index].x1, bez[bez_index].y1, bez[bez_index].x2, bez[bez_index].y2, bez[bez_index].x3, bez[bez_index].y3, flatness); x = bez[bez_index].x3; y = bez[bez_index].y3; break; } } while (bez[bez_index++].code != ART_END); return vec; } rl-renderpm-4.0.3/src/libart_lgpl/art_vpath_bpath.h000066400000000000000000000026471453236046100223630ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_VPATH_BPATH_H__ #define __ART_VPATH_BPATH_H__ #ifdef LIBART_COMPILATION #include "art_bpath.h" #include "art_vpath.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtPoint *art_bezier_to_vec (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, ArtPoint *p, int level); ArtVpath *art_bez_path_to_vec (const ArtBpath *bez, double flatness); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_VPATH_BPATH_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_vpath_dash.c000066400000000000000000000122161453236046100221700ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1999-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Apply a dash style to a vector path. */ #include "config.h" #include "art_vpath_dash.h" #include #include #include "art_misc.h" #include "art_vpath.h" /* Return the length of the largest subpath within vpath */ static int art_vpath_dash_max_subpath (const ArtVpath *vpath) { int max_subpath; int i; int start; max_subpath = 0; start = 0; for (i = 0; vpath[i].code != ART_END; i++) { if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN) { if (i - start > max_subpath) max_subpath = i - start; start = i; } } if (i - start > max_subpath) max_subpath = i - start; return max_subpath; } /** * art_vpath_dash: Add dash style to vpath. * @vpath: Original vpath. * @dash: Dash style. * * Creates a new vpath that is the result of applying dash style @dash * to @vpath. * * This implementation has two known flaws: * * First, it adds a spurious break at the beginning of the vpath. The * only way I see to resolve this flaw is to run the state forward one * dash break at the beginning, and fix up by looping back to the * first dash break at the end. This is doable but of course adds some * complexity. * * Second, it does not suppress output points that are within epsilon * of each other. * * Return value: Newly created vpath. **/ ArtVpath * art_vpath_dash (const ArtVpath *vpath, const ArtVpathDash *dash) { int max_subpath; double *dists; ArtVpath *result; int n_result, n_result_max; int start, end; int i; double total_dist; /* state while traversing dasharray - offset is offset of current dash value, toggle is 0 for "off" and 1 for "on", and phase is the distance in, >= 0, < dash->dash[offset]. */ int offset, toggle; double phase; /* initial values */ int offset_init, toggle_init; double phase_init; max_subpath = art_vpath_dash_max_subpath (vpath); dists = art_new (double, max_subpath); n_result = 0; n_result_max = 16; result = art_new (ArtVpath, n_result_max); /* determine initial values of dash state */ toggle_init = 1; offset_init = 0; phase_init = dash->offset; while (phase_init >= dash->dash[offset_init]) { toggle_init = !toggle_init; phase_init -= dash->dash[offset_init]; offset_init++; if (offset_init == dash->n_dash) offset_init = 0; } for (start = 0; vpath[start].code != ART_END; start = end) { for (end = start + 1; vpath[end].code == ART_LINETO; end++); /* subpath is [start..end) */ total_dist = 0; for (i = start; i < end - 1; i++) { double dx, dy; dx = vpath[i + 1].x - vpath[i].x; dy = vpath[i + 1].y - vpath[i].y; dists[i - start] = sqrt (dx * dx + dy * dy); total_dist += dists[i - start]; } if (total_dist <= dash->dash[offset_init] - phase_init) { /* subpath fits entirely within first dash */ if (toggle_init) { for (i = start; i < end; i++) art_vpath_add_point (&result, &n_result, &n_result_max, vpath[i].code, vpath[i].x, vpath[i].y); } } else { /* subpath is composed of at least one dash - thus all generated pieces are open */ double dist; phase = phase_init; offset = offset_init; toggle = toggle_init; dist = 0; i = start; if (toggle) art_vpath_add_point (&result, &n_result, &n_result_max, ART_MOVETO_OPEN, vpath[i].x, vpath[i].y); while (i != end - 1) { if (dists[i - start] - dist > dash->dash[offset] - phase) { /* dash boundary is next */ double a; double x, y; dist += dash->dash[offset] - phase; a = dist / dists[i - start]; x = vpath[i].x + a * (vpath[i + 1].x - vpath[i].x); y = vpath[i].y + a * (vpath[i + 1].y - vpath[i].y); art_vpath_add_point (&result, &n_result, &n_result_max, toggle ? ART_LINETO : ART_MOVETO_OPEN, x, y); /* advance to next dash */ toggle = !toggle; phase = 0; offset++; if (offset == dash->n_dash) offset = 0; } else { /* end of line in vpath is next */ phase += dists[i - start] - dist; i++; dist = 0; if (toggle) art_vpath_add_point (&result, &n_result, &n_result_max, ART_LINETO, vpath[i].x, vpath[i].y); } } } } art_vpath_add_point (&result, &n_result, &n_result_max, ART_END, 0, 0); art_free (dists); return result; } rl-renderpm-4.0.3/src/libart_lgpl/art_vpath_dash.h000066400000000000000000000025341453236046100221770ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1999 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_VPATH_DASH_H__ #define __ART_VPATH_DASH_H__ /* Apply a dash style to a vector path. */ #ifdef LIBART_COMPILATION #include "art_vpath.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _ArtVpathDash ArtVpathDash; struct _ArtVpathDash { double offset; int n_dash; double *dash; }; ArtVpath * art_vpath_dash (const ArtVpath *vpath, const ArtVpathDash *dash); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_VPATH_DASH_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/art_vpath_svp.c000066400000000000000000000117101453236046100220570ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998-2000 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* "Unsort" a sorted vector path into an ordinary vector path. */ #include "config.h" #include "art_vpath_svp.h" #include /* for printf - debugging */ #include "art_misc.h" #include "art_vpath.h" #include "art_svp.h" typedef struct _ArtVpathSVPEnd ArtVpathSVPEnd; struct _ArtVpathSVPEnd { int seg_num; int which; /* 0 = top, 1 = bottom */ double x, y; }; #define EPSILON 1e-6 static int art_vpath_svp_point_compare (double x1, double y1, double x2, double y2) { if (y1 - EPSILON > y2) return 1; if (y1 + EPSILON < y2) return -1; if (x1 - EPSILON > x2) return 1; if (x1 + EPSILON < x2) return -1; return 0; } static int art_vpath_svp_compare (const void *s1, const void *s2) { const ArtVpathSVPEnd *e1 = s1; const ArtVpathSVPEnd *e2 = s2; return art_vpath_svp_point_compare (e1->x, e1->y, e2->x, e2->y); } /* Convert from sorted vector path representation into regular vector path representation. Status of this routine: Basic correctness: Only works with closed paths. Numerical stability: Not known to work when more than two segments meet at a point. Speed: Should be pretty good. Precision: Does not degrade precision. */ /** * art_vpath_from_svp: Convert from svp to vpath form. * @svp: Original #ArtSVP. * * Converts the sorted vector path @svp into standard vpath form. * * Return value: the newly allocated vpath. **/ ArtVpath * art_vpath_from_svp (const ArtSVP *svp) { int n_segs = svp->n_segs; ArtVpathSVPEnd *ends; ArtVpath *new; int *visited; int n_new, n_new_max; int i, k; int j = 0; /* Quiet compiler */ int seg_num; int first; double last_x, last_y; int n_points; int pt_num; last_x = 0; /* to eliminate "uninitialized" warning */ last_y = 0; ends = art_new (ArtVpathSVPEnd, n_segs * 2); for (i = 0; i < svp->n_segs; i++) { int lastpt; ends[i * 2].seg_num = i; ends[i * 2].which = 0; ends[i * 2].x = svp->segs[i].points[0].x; ends[i * 2].y = svp->segs[i].points[0].y; lastpt = svp->segs[i].n_points - 1; ends[i * 2 + 1].seg_num = i; ends[i * 2 + 1].which = 1; ends[i * 2 + 1].x = svp->segs[i].points[lastpt].x; ends[i * 2 + 1].y = svp->segs[i].points[lastpt].y; } qsort (ends, n_segs * 2, sizeof (ArtVpathSVPEnd), art_vpath_svp_compare); n_new = 0; n_new_max = 16; /* I suppose we _could_ estimate this from traversing the svp, so we don't have to reallocate */ new = art_new (ArtVpath, n_new_max); visited = art_new (int, n_segs); for (i = 0; i < n_segs; i++) visited[i] = 0; first = 1; for (i = 0; i < n_segs; i++) { if (!first) { /* search for the continuation of the existing subpath */ /* This could be a binary search (which is why we sorted, above) */ for (j = 0; j < n_segs * 2; j++) { if (!visited[ends[j].seg_num] && art_vpath_svp_point_compare (last_x, last_y, ends[j].x, ends[j].y) == 0) break; } if (j == n_segs * 2) first = 1; } if (first) { /* start a new subpath */ for (j = 0; j < n_segs * 2; j++) if (!visited[ends[j].seg_num]) break; } if (j == n_segs * 2) { printf ("failure\n"); } seg_num = ends[j].seg_num; n_points = svp->segs[seg_num].n_points; for (k = 0; k < n_points; k++) { pt_num = svp->segs[seg_num].dir ? k : n_points - (1 + k); if (k == 0) { if (first) { art_vpath_add_point (&new, &n_new, &n_new_max, ART_MOVETO, svp->segs[seg_num].points[pt_num].x, svp->segs[seg_num].points[pt_num].y); } } else { art_vpath_add_point (&new, &n_new, &n_new_max, ART_LINETO, svp->segs[seg_num].points[pt_num].x, svp->segs[seg_num].points[pt_num].y); if (k == n_points - 1) { last_x = svp->segs[seg_num].points[pt_num].x; last_y = svp->segs[seg_num].points[pt_num].y; /* to make more robust, check for meeting first_[xy], set first if so */ } } first = 0; } visited[seg_num] = 1; } art_vpath_add_point (&new, &n_new, &n_new_max, ART_END, 0, 0); art_free (visited); art_free (ends); return new; } rl-renderpm-4.0.3/src/libart_lgpl/art_vpath_svp.h000066400000000000000000000024251453236046100220670ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ART_VPATH_SVP_H__ #define __ART_VPATH_SVP_H__ /* "Unsort" a sorted vector path into an ordinary vector path. */ #ifdef LIBART_COMPILATION #include "art_svp.h" #include "art_vpath.h" #else #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ ArtVpath *art_vpath_from_svp (const ArtSVP *svp); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __ART_VPATH_SVP_H__ */ rl-renderpm-4.0.3/src/libart_lgpl/autogen.sh000066400000000000000000000027721453236046100210410ustar00rootroot00000000000000#!/bin/sh # Run this to generate all the initial makefiles, etc. # This was lifted from the Gimp, and adapted slightly by # Raph Levien . DIE=0 srcdir=`dirname $0` test -z "$srcdir" && srcdir=. PROJECT=LibArt_LGPL (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have autoconf installed to compile $PROJECT." echo "Download the appropriate package for your distribution," echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" DIE=1 } # Do we really need libtool? (libtool --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have libtool installed to compile $PROJECT." echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2.tar.gz" echo "(or a newer version if it is available)" DIE=1 } (automake --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have automake installed to compile $PROJECT." echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" echo "(or a newer version if it is available)" DIE=1 } if test "$DIE" -eq 1; then exit 1 fi if test -z "$*"; then echo "I am going to run ./configure with no arguments - if you wish " echo "to pass any to it, please specify them on the $0 command line." fi case $CC in xlc ) am_opt=--include-deps;; esac for dir in $srcdir do echo processing $dir (cd $dir; \ aclocalinclude="$ACLOCAL_FLAGS"; \ aclocal $aclocalinclude; \ autoheader; automake --add-missing --gnu $am_opt; autoconf) done $srcdir/configure "$@" echo echo "Now type 'make' to compile $PROJECT." rl-renderpm-4.0.3/src/libart_lgpl/config.h000066400000000000000000000012341453236046100204510ustar00rootroot00000000000000/* config.h. Generated automatically by configure. */ /* config.h.in. Generated automatically from configure.in by autoheader. */ /* Define if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #ifdef macintosh # define WORDS_BIGENDIAN #else /* #undef WORDS_BIGENDIAN */ #endif /*allegedly this will take care of 'FAT' binaries in OS X*/ #if defined(__LITTLE_ENDIAN__) # undef WORDS_BIGENDIAN #elif defined(__BIG_ENDIAN__) # undef WORDS_BIGENDIAN # define WORDS_BIGENDIAN 1 #endif /* Name of package */ #define PACKAGE "libart_lgpl" /* Version number of package */ #define VERSION "2.3.10" rl-renderpm-4.0.3/src/libart_lgpl/configure.in000066400000000000000000000036151453236046100213510ustar00rootroot00000000000000AC_INIT(art_misc.h) AM_CONFIG_HEADER(config.h) LIBART_MAJOR_VERSION=2 LIBART_MINOR_VERSION=3 LIBART_MICRO_VERSION=12 LIBART_VERSION=$LIBART_MAJOR_VERSION.$LIBART_MINOR_VERSION.$LIBART_MICRO_VERSION LIBART_VERSION_INFO=`expr $LIBART_MAJOR_VERSION + $LIBART_MINOR_VERSION`:$LIBART_MICRO_VERSION:$LIBART_MINOR_VERSION AC_SUBST(LIBART_MAJOR_VERSION) AC_SUBST(LIBART_MINOR_VERSION) AC_SUBST(LIBART_MICRO_VERSION) AC_SUBST(LIBART_VERSION) AC_SUBST(LIBART_VERSION_INFO) VERSION=$LIBART_VERSION AM_INIT_AUTOMAKE(libart_lgpl, $VERSION) AC_ARG_ENABLE(ansi, [ --enable-ansi turn on strict ansi [default=no]], , enable_ansi=no) AC_PROG_CC AC_PROG_CPP AC_LIBTOOL_WIN32_DLL AM_PROG_LIBTOOL AM_MAINTAINER_MODE AC_MSG_CHECKING([for Win32]) case "$host" in *-*-mingw*) os_win32=yes ;; *) os_win32=no ;; esac AC_MSG_RESULT([$os_win32]) AM_CONDITIONAL(OS_WIN32, test "$os_win32" = "yes") if test "$os_win32" = "yes"; then AC_CHECK_PROG(ms_librarian, lib.exe, yes, no) fi AM_CONDITIONAL(MS_LIB_AVAILABLE, test x$ms_librarian = xyes) changequote(,)dnl if test "x$GCC" = "xyes"; then case " $CFLAGS " in *[\ \ ]-Wall[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wall" ;; esac case " $CFLAGS " in *[\ \ ]-Wmissing-prototypes[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wmissing-prototypes" ;; esac if test "x$enable_ansi" = "xyes"; then case " $CFLAGS " in *[\ \ ]-ansi[\ \ ]*) ;; *) CFLAGS="$CFLAGS -ansi" ;; esac case " $CFLAGS " in *[\ \ ]-pedantic[\ \ ]*) ;; *) CFLAGS="$CFLAGS -pedantic" ;; esac fi fi changequote([,])dnl AC_FUNC_ALLOCA LIBART_LIBDIR='-L${libdir}' LIBART_INCLUDEDIR='-I${includedir}' LIBART_LIBS='-lart_lgpl' AC_SUBST(LIBART_LIBDIR) AC_SUBST(LIBART_INCLUDEDIR) AC_SUBST(LIBART_LIBS) AC_C_BIGENDIAN AC_OUTPUT([ libart-features.h Makefile libart-config libart-2.0.pc libart-zip], [case "$CONFIG_FILES" in *libart-config*) chmod +x libart-config;; esac]) rl-renderpm-4.0.3/src/libart_lgpl/gen_art_config.c000066400000000000000000000023261453236046100221460ustar00rootroot00000000000000#include #include "config.h" #include /** * A little utility function to generate header info. * * Yes, it would be possible to do this using more "native" autoconf * features, but I personally find this approach to be cleaner. * * The output of this program is generally written to art_config.h, * which is installed in libart's include dir. **/ static void die (char *why) { fprintf (stderr, "gen_art_config: %s\n", why); exit (1); } int main (int argc, char **argv) { printf ("/* Automatically generated by gen_art_config.c */\n" "\n" "#define ART_SIZEOF_CHAR %d\n" "#define ART_SIZEOF_SHORT %d\n" "#define ART_SIZEOF_INT %d\n" "#define ART_SIZEOF_LONG %d\n" "\n", sizeof(char), sizeof(short), sizeof(int), sizeof(long)); if (sizeof(char) == 1) printf ("typedef unsigned char art_u8;\n"); else die ("sizeof(char) != 1"); if (sizeof(short) == 2) printf ("typedef unsigned short art_u16;\n"); else die ("sizeof(short) != 2"); if (sizeof(int) == 4) printf ("typedef unsigned int art_u32;\n"); else if (sizeof(long) == 4) printf ("typedef unsigned long art_u32;\n"); else die ("sizeof(int) != 4 and sizeof(long) != 4"); return 0; } rl-renderpm-4.0.3/src/libart_lgpl/libart-2.0.pc.in000066400000000000000000000003451453236046100215400ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libart Description: LGPL version of the libart library Version: @VERSION@ Libs: -L${libdir} -lart_lgpl_2 Cflags: -I${includedir}/libart-2.0 rl-renderpm-4.0.3/src/libart_lgpl/libart-config.in000066400000000000000000000017201453236046100221030ustar00rootroot00000000000000#!/bin/sh prefix=@prefix@ exec_prefix=@exec_prefix@ exec_prefix_set=no usage="\ Usage: libart-config [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--libs] [--cflags]" if test $# -eq 0; then echo "${usage}" 1>&2 exit 1 fi while test $# -gt 0; do case "$1" in -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) optarg= ;; esac case $1 in --prefix=*) prefix=$optarg if test $exec_prefix_set = no ; then exec_prefix=$optarg fi ;; --prefix) echo $prefix ;; --exec-prefix=*) exec_prefix=$optarg exec_prefix_set=yes ;; --exec-prefix) echo $exec_prefix ;; --version) echo @LIBART_VERSION@ ;; --cflags) includes=-I@includedir@/libart-2.0 echo $includes ;; --libs) libdirs=-L@libdir@ echo $libdirs -lart_lgpl_2 -lm ;; *) echo "${usage}" 1>&2 exit 1 ;; esac shift done rl-renderpm-4.0.3/src/libart_lgpl/libart-features.c000066400000000000000000000005561453236046100222760ustar00rootroot00000000000000#include "libart-features.h" /* General initialization hooks */ const unsigned int libart_major_version=LIBART_MAJOR_VERSION, libart_minor_version=LIBART_MINOR_VERSION, libart_micro_version=LIBART_MICRO_VERSION; const char *libart_version = LIBART_VERSION; void libart_preinit(void *app, void *modinfo) { } void libart_postinit(void *app, void *modinfo) { } rl-renderpm-4.0.3/src/libart_lgpl/libart-features.h.in000066400000000000000000000007521453236046100227060ustar00rootroot00000000000000#ifndef LIBART_FEATURES_H #define LIBART_FEATURES_H 1 #define LIBART_MAJOR_VERSION (@LIBART_MAJOR_VERSION@) #define LIBART_MINOR_VERSION (@LIBART_MINOR_VERSION@) #define LIBART_MICRO_VERSION (@LIBART_MICRO_VERSION@) #define LIBART_VERSION "@LIBART_VERSION@" extern const unsigned int libart_major_version, libart_minor_version, libart_micro_version; extern const char *libart_version; void libart_preinit(void *app, void *modinfo); void libart_postinit(void *app, void *modinfo); #endif rl-renderpm-4.0.3/src/libart_lgpl/libart.h000066400000000000000000000025071453236046100204650ustar00rootroot00000000000000#ifndef LIBART_H #define LIBART_H 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif rl-renderpm-4.0.3/src/libart_lgpl/libart.m4000066400000000000000000000141661453236046100205620ustar00rootroot00000000000000# Configure paths for LIBART # Raph Levien 98-11-18 # stolen from Manish Singh 98-9-30 # stolen back from Frank Belew # stolen from Manish Singh # Shamelessly stolen from Owen Taylor dnl AM_PATH_LIBART([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) dnl Test for LIBART, and define LIBART_CFLAGS and LIBART_LIBS dnl AC_DEFUN(AM_PATH_LIBART, [dnl dnl Get the cflags and libraries from the libart-config script dnl AC_ARG_WITH(libart-prefix,[ --with-libart-prefix=PFX Prefix where LIBART is installed (optional)], libart_prefix="$withval", libart_prefix="") AC_ARG_WITH(libart-exec-prefix,[ --with-libart-exec-prefix=PFX Exec prefix where LIBART is installed (optional)], libart_exec_prefix="$withval", libart_exec_prefix="") AC_ARG_ENABLE(libarttest, [ --disable-libarttest Do not try to compile and run a test LIBART program], , enable_libarttest=yes) if test x$libart_exec_prefix != x ; then libart_args="$libart_args --exec-prefix=$libart_exec_prefix" if test x${LIBART_CONFIG+set} != xset ; then LIBART_CONFIG=$libart_exec_prefix/bin/libart-config fi fi if test x$libart_prefix != x ; then libart_args="$libart_args --prefix=$libart_prefix" if test x${LIBART_CONFIG+set} != xset ; then LIBART_CONFIG=$libart_prefix/bin/libart-config fi fi AC_PATH_PROG(LIBART_CONFIG, libart-config, no) min_libart_version=ifelse([$1], ,0.2.5,$1) AC_MSG_CHECKING(for LIBART - version >= $min_libart_version) no_libart="" if test "$LIBART_CONFIG" = "no" ; then no_libart=yes else LIBART_CFLAGS=`$LIBART_CONFIG $libartconf_args --cflags` LIBART_LIBS=`$LIBART_CONFIG $libartconf_args --libs` libart_major_version=`$LIBART_CONFIG $libart_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` libart_minor_version=`$LIBART_CONFIG $libart_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` libart_micro_version=`$LIBART_CONFIG $libart_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x$enable_libarttest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $LIBART_CFLAGS" LIBS="$LIBS $LIBART_LIBS" dnl dnl Now check if the installed LIBART is sufficiently new. (Also sanity dnl checks the results of libart-config to some extent dnl rm -f conf.libarttest AC_TRY_RUN([ #include #include #include #include char* my_strdup (char *str) { char *new_str; if (str) { new_str = malloc ((strlen (str) + 1) * sizeof(char)); strcpy (new_str, str); } else new_str = NULL; return new_str; } int main () { int major, minor, micro; char *tmp_version; system ("touch conf.libarttest"); /* HP/UX 9 (%@#!) writes to sscanf strings */ tmp_version = my_strdup("$min_libart_version"); if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_libart_version"); exit(1); } if (($libart_major_version > major) || (($libart_major_version == major) && ($libart_minor_version > minor)) || (($libart_major_version == major) && ($libart_minor_version == minor) && ($libart_micro_version >= micro))) { return 0; } else { printf("\n*** 'libart-config --version' returned %d.%d.%d, but the minimum version\n", $libart_major_version, $libart_minor_version, $libart_micro_version); printf("*** of LIBART required is %d.%d.%d. If libart-config is correct, then it is\n", major, minor, micro); printf("*** best to upgrade to the required version.\n"); printf("*** If libart-config was wrong, set the environment variable LIBART_CONFIG\n"); printf("*** to point to the correct copy of libart-config, and remove the file\n"); printf("*** config.cache before re-running configure\n"); return 1; } } ],, no_libart=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_libart" = x ; then AC_MSG_RESULT(yes) ifelse([$2], , :, [$2]) else AC_MSG_RESULT(no) if test "$LIBART_CONFIG" = "no" ; then echo "*** The libart-config script installed by LIBART could not be found" echo "*** If LIBART was installed in PREFIX, make sure PREFIX/bin is in" echo "*** your path, or set the LIBART_CONFIG environment variable to the" echo "*** full path to libart-config." else if test -f conf.libarttest ; then : else echo "*** Could not run LIBART test program, checking why..." CFLAGS="$CFLAGS $LIBART_CFLAGS" LIBS="$LIBS $LIBART_LIBS" AC_TRY_LINK([ #include #include ], [ return 0; ], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding LIBART or finding the wrong" echo "*** version of LIBART. If it is not finding LIBART, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means LIBART was incorrectly installed" echo "*** or that you have moved LIBART since it was installed. In the latter case, you" echo "*** may want to edit the libart-config script: $LIBART_CONFIG" ]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi LIBART_CFLAGS="" LIBART_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(LIBART_CFLAGS) AC_SUBST(LIBART_LIBS) rm -f conf.libarttest ]) rl-renderpm-4.0.3/src/libart_lgpl/libartConf.sh.in000066400000000000000000000002601453236046100220550ustar00rootroot00000000000000# # Configuration file for using the LIBART library in GNOME applications # LIBART_LIBDIR="@LIBART_LIBDIR@" LIBART_LIBS="@LIBART_LIBS@" LIBART_INCLUDEDIR="@LIBART_INCLUDEDIR@" rl-renderpm-4.0.3/src/libart_lgpl/test_gradient.c000066400000000000000000000205221453236046100220340ustar00rootroot00000000000000/* test_gradient.c */ #include #include #include "art_point.h" #include "art_misc.h" #include "art_affine.h" #include "art_svp.h" #include "art_svp_vpath.h" #include "art_rgb.h" #include "art_rgb_svp.h" #include "art_render.h" #include "art_render_gradient.h" GtkWidget *drawing_area; static art_u8 *rgbdata = NULL; int width, height; double pos_x[2], pos_y[2]; int pos_nr = 0; double a = 1/32.0; double b = -1/33.00000; double c = -1/3.0; ArtGradientSpread spread = ART_GRADIENT_REPEAT; ArtVpath * randstar (int n) { ArtVpath *vec; int i; double r, th; vec = art_new (ArtVpath, n + 2); for (i = 0; i < n; i++) { vec[i].code = i ? ART_LINETO : ART_MOVETO; r = rand () * (250.0 / RAND_MAX); th = i * 2 * M_PI / n; vec[i].x = 250 + r * cos (th); vec[i].y = 250 - r * sin (th); } vec[i].code = ART_LINETO; vec[i].x = vec[0].x; vec[i].y = vec[0].y; i++; vec[i].code = ART_END; vec[i].x = 0; vec[i].y = 0; return vec; } ArtVpath * rect (void) { ArtVpath *vec; int i; double r, th; #define START 0 vec = art_new (ArtVpath, 6); vec[0].code = ART_MOVETO; vec[0].x = START; vec[0].y = START; vec[1].code = ART_LINETO; vec[1].x = START; vec[1].y = 512-START; vec[2].code = ART_LINETO; vec[2].x = 512-START; vec[2].y = 512-START; vec[3].code = ART_LINETO; vec[3].x = 512-START; vec[3].y = START; vec[4].code = ART_LINETO; vec[4].x = START; vec[4].y = START; vec[5].code = ART_END; vec[5].x = 0; vec[5].y = 0; return vec; } static void draw_test() { static ArtVpath *vpath = NULL; static ArtSVP *svp = NULL; ArtRender *render; ArtPixMaxDepth color[3] = {0x0000, 0x0000, 0x8000 }; ArtGradientLinear gradient; ArtGradientStop stops[3] = { { 0.02, { 0x7fff, 0x0000, 0x0000, 0x7fff }}, { 0.5, { 0x0000, 0x0000, 0x0000, 0x1000 }}, { 0.98, { 0x0000, 0x7fff, 0x0000, 0x7fff }} }; if (!vpath) { vpath = rect (); svp = art_svp_from_vpath (vpath); } gradient.a = a; gradient.b = b; gradient.c = c; gradient.spread = spread; gradient.n_stops = sizeof(stops) / sizeof(stops[0]); gradient.stops = stops; render = art_render_new (0, 0, width, height, rgbdata, width * 3, 3, 8, ART_ALPHA_NONE, NULL); art_render_clear_rgb (render, 0xfff0c0); art_render_svp (render, svp); art_render_gradient_linear (render, &gradient, ART_FILTER_NEAREST); // art_render_image_solid (render, color); art_render_invoke (render); } /* Create a new backing pixmap of the appropriate size */ static gint configure_event( GtkWidget *widget, GdkEventConfigure *event ) { if (rgbdata) free(rgbdata); rgbdata = malloc(3*widget->allocation.width*widget->allocation.height); width = widget->allocation.width; height = widget->allocation.height; draw_test(); return TRUE; } /* Redraw the screen from the backing pixmap */ static gint expose_event( GtkWidget *widget, GdkEventExpose *event ) { static GdkGC *copy_gc = NULL; if (copy_gc == NULL) { copy_gc = gdk_gc_new(widget->window); } gdk_draw_rgb_image(widget->window, copy_gc, event->area.x, event->area.y, event->area.width, event->area.height, GDK_RGB_DITHER_NONE, rgbdata + event->area.x*3+event->area.y*3*width, width*3 ); return FALSE; } static gint button_press_event( GtkWidget *widget, GdkEventButton *event ) { static GdkGC *copy_gc = NULL; int x, y; x = event->x; y = event->y; pos_x[pos_nr] = (double) x; pos_y[pos_nr] = (double) y; pos_nr = (pos_nr+1) % 2; draw_test(); if (copy_gc == NULL) { copy_gc = gdk_gc_new(widget->window); } gdk_draw_rgb_image(widget->window, copy_gc, 0, 0, width, height, GDK_RGB_DITHER_NONE, rgbdata, width*3 ); return TRUE; } static gint motion_notify_event( GtkWidget *widget, GdkEventMotion *event ) { static GdkGC *copy_gc = NULL; int x, y; GdkModifierType state; if (event->is_hint) { gdk_window_get_pointer (event->window, &x, &y, &state); } else { x = event->x; y = event->y; state = event->state; } if (state & GDK_BUTTON1_MASK && rgbdata != NULL) { pos_x[(pos_nr+1) % 2] = (double) x; pos_y[(pos_nr+1) % 2] = (double) y; draw_test(); if (copy_gc == NULL) { copy_gc = gdk_gc_new(widget->window); } gdk_draw_rgb_image(widget->window, copy_gc, 0, 0, width, height, GDK_RGB_DITHER_NONE, rgbdata, width*3 ); } return TRUE; } void quit () { gtk_exit (0); } static void change_gradient (GtkEntry *entry, double *ptr) { double d; d = g_ascii_strtod (gtk_entry_get_text (entry), NULL); if (d != 0.0) *ptr = 1.0/d; else *ptr = 0.0; draw_test(); gtk_widget_queue_draw (drawing_area); } static void change_spread (GtkEntry *entry, int *ptr) { double d; d = g_ascii_strtod (gtk_entry_get_text (entry), NULL); *ptr = (int)d; draw_test(); gtk_widget_queue_draw (drawing_area); } int main( int argc, char *argv[] ) { GtkWidget *window; GtkWidget *vbox; char buf[G_ASCII_DTOSTR_BUF_SIZE]; GtkWidget *button; GtkWidget *entry; gtk_init (&argc, &argv); gdk_rgb_init(); pos_x[0] = 100; pos_y[0] = 100; pos_x[1] = 300; pos_y[1] = 300; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_set_name (window, "Test Input"); vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (window), vbox); gtk_widget_show (vbox); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (quit), NULL); /* Create the drawing area */ drawing_area = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 512, 512); gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0); gtk_widget_show (drawing_area); /* Signals used to handle backing pixmap */ gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event", (GtkSignalFunc) expose_event, NULL); gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event", (GtkSignalFunc) configure_event, NULL); /* Event signals */ gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event", (GtkSignalFunc) motion_notify_event, NULL); gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event", (GtkSignalFunc) button_press_event, NULL); gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (entry), "activate", GTK_SIGNAL_FUNC (change_gradient), &a); gtk_entry_set_text (GTK_ENTRY (entry), g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, 1.0/a)); gtk_widget_show (entry); entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (entry), "activate", GTK_SIGNAL_FUNC (change_gradient), &b); gtk_entry_set_text (GTK_ENTRY (entry), g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, 1.0/b)); gtk_widget_show (entry); entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (entry), "activate", GTK_SIGNAL_FUNC (change_gradient), &c); gtk_entry_set_text (GTK_ENTRY (entry), g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, 1.0/c)); gtk_widget_show (entry); entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (entry), "activate", GTK_SIGNAL_FUNC (change_spread), &spread); gtk_entry_set_text (GTK_ENTRY (entry), g_strdup_printf ("%d", spread)); gtk_widget_show (entry); /* .. And a quit button */ button = gtk_button_new_with_label ("Quit"); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); gtk_widget_show (button); gtk_widget_show (window); gtk_main (); return 0; } rl-renderpm-4.0.3/src/libart_lgpl/testart.c000066400000000000000000000360611453236046100206730ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998, 1999 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include "art_misc.h" #include "art_vpath.h" #include "art_svp.h" #include "art_svp_vpath.h" #include "art_gray_svp.h" #include "art_rgb_svp.h" #include "art_svp_vpath_stroke.h" #include "art_svp_ops.h" #include "art_affine.h" #include "art_rgb_affine.h" #include "art_rgb_bitmap_affine.h" #include "art_rgb_rgba_affine.h" #include "art_alphagamma.h" #include "art_svp_point.h" #include "art_vpath_dash.h" #include "art_render.h" #include "art_render_gradient.h" #include "art_render_svp.h" #include "art_svp_intersect.h" #ifdef DEAD_CODE static void test_affine (void) { double src[6]; double dst[6]; double src2[6]; char str[128]; int i; ArtPoint ps, pd, ptmp; for (i = 0; i < 6; i++) { src[i] = (rand () * 2.0 / RAND_MAX) - 1.0; src2[i] = (rand () * 2.0 / RAND_MAX) - 1.0; } #if 0 src[0] = 0.9999999; src[1] = -0.000001; src[2] = 0.000001; src[3] = 0.9999999; src[4] = 0; src[5] = 0; #if 1 src[0] = 0.98480775; src[1] = -0.17364818; src[2] = 0.17364818; src[3] = 0.98480775; #endif src2[0] = 0.98480775; src2[1] = -0.17364818; src2[2] = 0.17364818; src2[3] = 0.98480775; #endif ps.x = rand() * 100.0 / RAND_MAX; ps.y = rand() * 100.0 / RAND_MAX; art_affine_point (&pd, &ps, src); art_affine_invert (dst, src); art_affine_point (&ptmp, &pd, dst); art_affine_to_string (str, src); printf ("src = %s\n", str); art_affine_to_string (str, dst); printf ("dst = %s\n", str); printf ("point (%g, %g) -> (%g, %g) -> (%g, %g)\n", ps.x, ps.y, pd.x, pd.y, ptmp.x, ptmp.y); art_affine_point (&ptmp, &ps, src); art_affine_point (&pd, &ptmp, src2); art_affine_to_string (str, src2); printf ("src2 = %s\n", str); printf ("point (%g, %g) -> (%g, %g) -> (%g, %g)\n", ps.x, ps.y, ptmp.x, ptmp.y, pd.x, pd.y); art_affine_multiply (dst, src, src2); art_affine_to_string (str, dst); printf ("dst = %s\n", str); art_affine_point (&pd, &ps, dst); printf ("point (%g, %g) -> (%g, %g)\n", ps.x, ps.y, pd.x, pd.y); } #endif static ArtVpath * randstar (int n) { ArtVpath *vec; int i; double r, th; vec = art_new (ArtVpath, n + 2); for (i = 0; i < n; i++) { vec[i].code = i ? ART_LINETO : ART_MOVETO; r = rand () * (250.0 / RAND_MAX); #if 0 r = r + 0.9 * (250 - r); #endif th = i * 2 * M_PI / n; vec[i].x = 250 + r * cos (th); vec[i].y = 250 - r * sin (th); } vec[i].code = ART_LINETO; vec[i].x = vec[0].x; vec[i].y = vec[0].y; i++; vec[i].code = ART_END; vec[i].x = 0; vec[i].y = 0; return vec; } #define TILE_SIZE 512 #define NUM_ITERS 1 #define COLOR #ifdef COLOR #define BYTES_PP 3 #else #define BYTES_PP 1 #endif #ifndef nDEAD_CODE void print_svp (ArtSVP *vp) { int i, j; for (i = 0; i < vp->n_segs; i++) { printf ("segment %d, dir = %s (%f, %f) - (%f, %f)\n", i, vp->segs[i].dir ? "down" : "up", vp->segs[i].bbox.x0, vp->segs[i].bbox.y0, vp->segs[i].bbox.x1, vp->segs[i].bbox.y1); for (j = 0; j < vp->segs[i].n_points; j++) printf (" (%g, %g)\n", vp->segs[i].points[j].x, vp->segs[i].points[j].y); } } #endif static void print_vpath (ArtVpath *vpath) { int i; for (i = 0; vpath[i].code != ART_END; i++) printf ("%g %g %s\n", vpath[i].x, vpath[i].y, vpath[i].code == ART_MOVETO_OPEN ? "moveto %open" : vpath[i].code == ART_MOVETO ? "moveto" : vpath[i].code == ART_LINETO ? "lineto" : "?"); printf ("stroke\n"); } static void make_testpat (void) { ArtVpath *vpath, *vpath2, *vpath3; ArtSVP *svp, *svp2; ArtSVP *svp3; art_u8 buf[512 * 512 * BYTES_PP]; int i, j; int iter; art_u8 colorimg[256][256][3]; art_u8 rgbaimg[256][256][4]; art_u8 bitimg[16][2]; int x, y; double affine[6]; double affine2[6]; double affine3[6]; ArtAlphaGamma *alphagamma; double dash_data[] = { 20 }; ArtVpathDash dash; dash.offset = 0; dash.n_dash = 1; dash.dash = dash_data; #ifdef TEST_AFFINE test_affine (); exit (0); #endif vpath = randstar (50); svp = art_svp_from_vpath (vpath); art_free (vpath); vpath2 = randstar (50); #if 1 vpath3 = art_vpath_dash (vpath2, &dash); art_free (vpath2); svp2 = art_svp_vpath_stroke (vpath3, ART_PATH_STROKE_JOIN_MITER, ART_PATH_STROKE_CAP_BUTT, 15, 4, 0.5); art_free (vpath3); #else svp2 = art_svp_from_vpath (vpath2); #endif #if 1 svp3 = art_svp_intersect (svp, svp2); #else svp3 = svp2; #endif #if 0 print_svp (svp); #endif for (y = 0; y < 256; y++) for (x = 0; x < 256; x++) { colorimg[y][x][0] = (x + y) >> 1; colorimg[y][x][1] = (x + (255 - y)) >> 1; colorimg[y][x][2] = ((255 - x) + y) >> 1; rgbaimg[y][x][0] = (x + y) >> 1; rgbaimg[y][x][1] = (x + (255 - y)) >> 1; rgbaimg[y][x][2] = ((255 - x) + y) >> 1; rgbaimg[y][x][3] = y; } for (y = 0; y < 16; y++) for (x = 0; x < 2; x++) bitimg[y][x] = (x << 4) | y; affine[0] = 0.5; affine[1] = .2; affine[2] = -.2; affine[3] = 0.5; affine[4] = 64; affine[5] = 64; affine2[0] = 1; affine2[1] = -.2; affine2[2] = .2; affine2[3] = 1; affine2[4] = 128; affine2[5] = 128; affine3[0] = 5; affine3[1] = -.2; affine3[2] = .2; affine3[3] = 5; affine3[4] = 384; affine3[5] = 32; #if 0 alphagamma = art_alphagamma_new (1.8); #else alphagamma = NULL; #endif #ifdef COLOR printf ("P6\n512 512\n255\n"); #else printf ("P5\n512 512\n255\n"); #endif for (iter = 0; iter < NUM_ITERS; iter++) for (j = 0; j < 512; j += TILE_SIZE) for (i = 0; i < 512; i += TILE_SIZE) { #ifdef COLOR art_rgb_svp_aa (svp, i, j, i + TILE_SIZE, j + TILE_SIZE, 0xffe0a0, 0x100040, buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP, alphagamma); art_rgb_svp_alpha (svp2, i, j, i + TILE_SIZE, j + TILE_SIZE, 0xff000080, buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP, alphagamma); art_rgb_svp_alpha (svp3, i, j, i + TILE_SIZE, j + TILE_SIZE, 0x00ff0080, buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP, alphagamma); art_rgb_affine (buf + (j * 512 + i) * BYTES_PP, i, j, i + TILE_SIZE, j + TILE_SIZE, 512 * BYTES_PP, (art_u8 *)colorimg, 256, 256, 256 * 3, affine, ART_FILTER_NEAREST, alphagamma); art_rgb_rgba_affine (buf + (j * 512 + i) * BYTES_PP, i, j, i + TILE_SIZE, j + TILE_SIZE, 512 * BYTES_PP, (art_u8 *)rgbaimg, 256, 256, 256 * 4, affine2, ART_FILTER_NEAREST, alphagamma); art_rgb_bitmap_affine (buf + (j * 512 + i) * BYTES_PP, i, j, i + TILE_SIZE, j + TILE_SIZE, 512 * BYTES_PP, (art_u8 *)bitimg, 16, 16, 2, 0xffff00ff, affine3, ART_FILTER_NEAREST, alphagamma); #else art_gray_svp_aa (svp, i, j, i + TILE_SIZE, j + TILE_SIZE, buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP); #endif } art_svp_free (svp2); art_svp_free (svp3); art_svp_free (svp); #if 1 fwrite (buf, 1, 512 * 512 * BYTES_PP, stdout); #endif } static void test_dist (void) { ArtVpath *vpath; ArtSVP *svp; art_u8 buf[512 * 512 * BYTES_PP]; int x, y; int ix; double dist; int wind; vpath = randstar (20); #ifdef NO_STROKE svp = art_svp_from_vpath (vpath); #else svp = art_svp_vpath_stroke (vpath, ART_PATH_STROKE_JOIN_MITER, ART_PATH_STROKE_CAP_BUTT, 15, 4, 0.5); #endif art_rgb_svp_aa (svp, 0, 0, 512, 512, 0xffe0a0, 0x100040, buf, 512 * BYTES_PP, NULL); ix = 0; for (y = 0; y < 512; y++) { for (x = 0; x < 512; x++) { wind = art_svp_point_wind (svp, x, y); buf[ix] = 204 - wind * 51; dist = art_svp_point_dist (svp, x, y); if (((x | y) & 0x3f) == 0) { fprintf (stderr, "%d,%d: %f\n", x, y, dist); } buf[ix + 1] = 255 - dist; ix += 3; } } printf ("P6\n512 512\n255\n"); fwrite (buf, 1, 512 * 512 * BYTES_PP, stdout); } static void test_dash (void) { ArtVpath *vpath, *vpath2; double dash_data[] = { 10, 4, 1, 4}; ArtVpathDash dash; dash.offset = 0; dash.n_dash = 3; dash.dash = dash_data; vpath = randstar (50); vpath2 = art_vpath_dash (vpath, &dash); printf ("%%!\n"); print_vpath (vpath2); printf ("showpage\n"); art_free (vpath); art_free (vpath2); } static void test_render_gradient (art_u8 *buf) { ArtGradientLinear gradient; ArtGradientStop stops[3] = { { 0.0, { 0x7fff, 0x0000, 0x0000, 0x7fff }}, { 0.5, { 0x0000, 0x0000, 0x0000, 0x1000 }}, { 1.0, { 0x0000, 0x7fff, 0x0000, 0x7fff }} }; ArtVpath *vpath; ArtSVP *svp; ArtRender *render; gradient.a = 0.003; gradient.b = -0.0015; gradient.c = 0.1; gradient.spread = ART_GRADIENT_PAD; gradient.n_stops = sizeof(stops) / sizeof(stops[0]); gradient.stops = stops; vpath = randstar (50); svp = art_svp_from_vpath (vpath); render = art_render_new (0, 0, 512, 512, buf, 512 * 3, 3, 8, ART_ALPHA_NONE, NULL); art_render_svp (render, svp); art_render_gradient_linear (render, &gradient, ART_FILTER_NEAREST); art_render_invoke (render); } static void test_render_rad_gradient (art_u8 *buf) { ArtGradientRadial gradient; ArtGradientStop stops[3] = { { 0.0, { 0xffff, 0x0000, 0x0000, 0xffff }}, { 0.5, { 0xe000, 0xe000, 0x0000, 0xe000 }}, { 1.0, { 0x0000, 0x0000, 0x0000, 0x0000 }} }; ArtVpath *vpath; ArtSVP *svp; ArtRender *render; gradient.affine[0] = 3.0 / 512; gradient.affine[1] = 0; gradient.affine[2] = 0; gradient.affine[3] = 3.0 / 512; gradient.affine[4] = -1.5; gradient.affine[5] = -1.5; gradient.fx = 0.9; gradient.fy = 0.1; gradient.n_stops = sizeof(stops) / sizeof(stops[0]); gradient.stops = stops; vpath = randstar (50); svp = art_svp_from_vpath (vpath); render = art_render_new (0, 0, 512, 512, buf, 512 * 3, 3, 8, ART_ALPHA_NONE, NULL); art_render_svp (render, svp); art_render_gradient_radial (render, &gradient, ART_FILTER_NEAREST); art_render_invoke (render); } static void test_gradient (void) { ArtVpath *vpath; ArtSVP *svp; art_u8 buf[512 * 512 * 3]; ArtRender *render; ArtPixMaxDepth color[3] = {0x0000, 0x0000, 0x8000 }; int i; const int n_iter = 1; vpath = randstar (50); svp = art_svp_from_vpath (vpath); for (i = 0; i < n_iter; i++) { #define USE_RENDER #ifdef USE_RENDER render = art_render_new (0, 0, 512, 512, buf, 512 * 3, 3, 8, ART_ALPHA_NONE, NULL); art_render_clear_rgb (render, 0xfff0c0); art_render_svp (render, svp); art_render_image_solid (render, color); art_render_invoke (render); #else art_rgb_svp_aa (svp, 0, 0, 512, 512, 0xfff0c0, 0x000080, buf, 512 * 3, NULL); #endif } #if 1 test_render_gradient (buf); #endif test_render_rad_gradient (buf); printf ("P6\n512 512\n255\n"); fwrite (buf, 1, 512 * 512 * 3, stdout); } static void output_svp_ppm (const ArtSVP *svp) { art_u8 buf[512 * 512 * 3]; art_rgb_svp_aa (svp, 0, 0, 512, 512, 0xfff0c0, 0x000080, buf, 512 * 3, NULL); printf ("P6\n512 512\n255\n"); fwrite (buf, 1, 512 * 512 * 3, stdout); } static void test_intersect (void) { ArtVpath vpath[] = { #if 0 /* two triangles */ { ART_MOVETO, 100, 100 }, { ART_LINETO, 300, 400 }, { ART_LINETO, 400, 200 }, { ART_LINETO, 100, 100 }, { ART_MOVETO, 110, 110 }, { ART_LINETO, 310, 410 }, { ART_LINETO, 410, 210 }, { ART_LINETO, 110, 110 }, #endif #if 0 /* a bowtie */ { ART_MOVETO, 100, 100 }, { ART_LINETO, 400, 400 }, { ART_LINETO, 400, 100 }, { ART_LINETO, 100, 400 }, { ART_LINETO, 100, 100 }, #endif #if 1 /* a square */ { ART_MOVETO, 100, 100 }, { ART_LINETO, 100, 400 }, { ART_LINETO, 400, 400 }, { ART_LINETO, 400, 100 }, { ART_LINETO, 100, 100 }, #endif #if 1 /* another square */ #define XOFF 10 #define YOFF 10 { ART_MOVETO, 100 + XOFF, 100 + YOFF }, { ART_LINETO, 100 + XOFF, 400 + YOFF }, { ART_LINETO, 400 + XOFF, 400 + YOFF }, { ART_LINETO, 400 + XOFF, 100 + YOFF }, { ART_LINETO, 100 + XOFF, 100 + YOFF }, #endif { ART_END, 0, 0} }; ArtSVP *svp, *svp2; ArtSvpWriter *swr; svp = art_svp_from_vpath (vpath); #define RUN_INTERSECT #ifdef RUN_INTERSECT swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN); art_svp_intersector (svp, swr); svp2 = art_svp_writer_rewind_reap (swr); #endif #if 0 output_svp_ppm (svp2); #else print_svp (svp2); #endif art_svp_free (svp); #ifdef RUN_INTERSECT art_svp_free (svp2); #endif } static void test_intersection(void) { ArtVpath vp0[] = { { ART_MOVETO, 10, 10}, { ART_LINETO, 60, 60}, { ART_LINETO, 110, 10}, { ART_LINETO, 10, 10}, { ART_END, 0, 0} }, vp1[]= { {ART_MOVETO, 110,34.999999999999993}, {ART_LINETO, 10,35}, {ART_LINETO, 10,36}, {ART_LINETO, 110,35.999999999999993}, {ART_LINETO, 110,34.999999999999993}, {ART_END, 0,0} }, vp2[]={ {ART_MOVETO, 110,35.999999999999993}, {ART_LINETO, 10,36}, {ART_LINETO, 10,37}, {ART_LINETO, 110,36.999999999999993}, {ART_LINETO, 110,35.999999999999993}, {ART_END, 0,0} }, *vp[] = {vp0, vp1, vp2}; ArtSVP *svp[3], *svpi; int i; for(i=0;i<3;i++){ svp[i]=art_svp_from_vpath(vp[i]); printf("vpath[%d]\n", i); print_vpath(vp[i]); printf("svp[%d]\n", i); print_svp(svp[i]); } for(i=2;i>0;i--){ svpi = art_svp_intersect(svp[i],svp[0]); printf("svp[%d] & svp[0]\n", i); print_svp(svpi); art_svp_free(svpi); } for(i=0;i<3;i++){ art_svp_free(svp[i]); } } static void usage (void) { fprintf (stderr, "usage: testart \n" " where is one of:\n" " testpat -- make random star + gradients test pattern\n" " gradient -- test pattern for rendered gradients\n" " dash -- dash test (output is valid PostScript)\n" " dist -- distance test\n" " intersect -- softball test for intersector\n" " intersect1 -- intersection test\n"); exit (1); } int main (int argc, char **argv) { if (argc < 2) usage (); if (!strcmp (argv[1], "testpat")) make_testpat (); else if (!strcmp (argv[1], "gradient")) test_gradient (); else if (!strcmp (argv[1], "dist")) test_dist (); else if (!strcmp (argv[1], "dash")) test_dash (); else if (!strcmp (argv[1], "intersect")) test_intersect (); else if (!strcmp (argv[1], "intersect1")) test_intersection(); else usage (); return 0; } rl-renderpm-4.0.3/src/libart_lgpl/testuta.c000066400000000000000000000125501453236046100206730ustar00rootroot00000000000000/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include "art_misc.h" #include "art_uta.h" #include "art_vpath.h" #include "art_uta_vpath.h" #include "art_rect.h" #include "art_rect_uta.h" #include "art_uta_rect.h" #define TEST_UTA #define noTEST_UTA_SPEED #define XOFF 50 #define YOFF 700 static void print_uta_ps (ArtUta *uta) { int x, y; int x0, y0, x1, y1; int width = uta->width; ArtUtaBbox ub; for (y = 0; y < uta->height; y++) for (x = 0; x < width; x++) { ub = uta->utiles[y * width + x]; if (ub != 0) { x0 = (uta->x0 + x) * ART_UTILE_SIZE + ART_UTA_BBOX_X0(ub); x1 = (uta->x0 + x) * ART_UTILE_SIZE + ART_UTA_BBOX_X1(ub); y0 = (uta->y0 + y) * ART_UTILE_SIZE + ART_UTA_BBOX_Y0(ub); y1 = (uta->y0 + y) * ART_UTILE_SIZE + ART_UTA_BBOX_Y1(ub); printf ("%% tile %d, %d: %d %d %d %d\n", x, y, ART_UTA_BBOX_X0(ub), ART_UTA_BBOX_Y0(ub), ART_UTA_BBOX_X1(ub), ART_UTA_BBOX_Y1(ub)); printf ("%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath fill\n", XOFF + x0, YOFF - y0, XOFF + x1, YOFF - y0, XOFF + x1, YOFF - y1, XOFF + x0, YOFF - y1); } } } static void print_rbuf_ps (int *rbuf, int width, int height) { int x, y; for (y = 0; y < height; y++) for (x = 0; x < width; x++) if (1 && rbuf[y * width + x] != 0) printf ("%d %d moveto (%d) show\n", x * ART_UTILE_SIZE, y * ART_UTILE_SIZE, rbuf[y * width + x]); } #if 0 void randline (ArtUta *uta, int *rbuf, int rbuf_rowstride) { double x0, y0, x1, y1; x0 = rand () * (500.0 / RAND_MAX); y0 = rand () * (500.0 / RAND_MAX); x1 = rand () * (500.0 / RAND_MAX); y1 = rand () * (500.0 / RAND_MAX); printf ("%g %g moveto %g %g lineto stroke\n", x0, y0, x1, y1); art_uta_add_line (uta, x0, y0, x1, y1, rbuf, rbuf_rowstride); } #endif static void print_ps_vpath (ArtVpath *vpath) { int i; for (i = 0; vpath[i].code != ART_END; i++) { switch (vpath[i].code) { case ART_MOVETO: printf ("%g %g moveto\n", XOFF + vpath[i].x, YOFF - vpath[i].y); break; case ART_LINETO: printf ("%g %g lineto\n", XOFF + vpath[i].x, YOFF - vpath[i].y); break; default: break; } } printf ("stroke\n"); } static ArtVpath * randstar (int n) { ArtVpath *vec; int i; double r, th; vec = art_new (ArtVpath, n + 2); for (i = 0; i < n; i++) { vec[i].code = i ? ART_LINETO : ART_MOVETO; r = rand () * (250.0 / RAND_MAX); th = i * 2 * M_PI / n; vec[i].x = 250 + r * cos (th); vec[i].y = 250 - r * sin (th); } vec[i].code = ART_LINETO; vec[i].x = vec[0].x; vec[i].y = vec[0].y; i++; vec[i].code = ART_END; vec[i].x = 0; vec[i].y = 0; return vec; } int main (int argc, char **argv) { ArtUta *uta; int i; int *rbuf; ArtVpath *vec; ArtIRect *rects; int n_rects; if (argc == 2) srand (atoi (argv[1])); #ifdef TEST_UTA printf ("%%!PS-Adobe\n"); printf ("/Helvetica findfont 12 scalefont setfont\n"); printf ("0.5 setlinewidth\n"); printf ("0.5 setgray\n"); for (i = 0; i < 500; i += ART_UTILE_SIZE) { printf ("%d %d moveto %d %d lineto stroke\n", XOFF, YOFF - i, XOFF + 500, YOFF - i); printf ("%d %d moveto %d %d lineto stroke\n", XOFF + i, YOFF, XOFF + i, YOFF - 500); } printf ("/a {\n"); #if 1 vec = randstar (50); print_ps_vpath (vec); uta = art_uta_from_vpath (vec); #ifdef TEST_UTA_RECT { ArtIRect bbox = {5, 5, 450, 450}; uta = art_uta_from_irect (&bbox); } #endif rbuf = 0; #else uta = art_uta_new_coords (0, 0, 500, 500); rbuf = malloc (sizeof(int) * (500 >> ART_UTILE_SHIFT) * (500 >> ART_UTILE_SHIFT)); for (i = 0; i < 10; i++) randline (uta, rbuf, 500 >> ART_UTILE_SHIFT); #endif printf ("} def 1 0.5 0.5 setrgbcolor\n"); print_uta_ps (uta); printf ("0 0 0.5 setrgbcolor\n"); if (rbuf) print_rbuf_ps (rbuf, 500 >> ART_UTILE_SHIFT, 500 >> ART_UTILE_SHIFT); printf ("0 setgray a\n"); rects = art_rect_list_from_uta (uta, 256, 64, &n_rects); printf ("%% %d rectangles:\n0 0 1 setrgbcolor\n", n_rects); for (i = 0; i < n_rects; i++) printf ("%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n", XOFF + rects[i].x0, YOFF - rects[i].y0, XOFF + rects[i].x1, YOFF - rects[i].y0, XOFF + rects[i].x1, YOFF - rects[i].y1, XOFF + rects[i].x0, YOFF - rects[i].y1); printf ("showpage\n"); #endif #ifdef TEST_UTA_SPEED for (i = 0; i < 1000; i++) { vec = randstar (50); uta = art_uta_from_vpath (vec); art_free (vec); art_uta_free (uta); } #endif return 0; } rl-renderpm-4.0.3/src/pfm.py000066400000000000000000000575061453236046100157110ustar00rootroot00000000000000import struct class _BUILDER: '''Virtual base helper class for structured file scanning''' def _get_struct_fmt(self,info): fmt = '<' for f, _, _ in info: fmt += f return fmt def _scan_from_file(self,f,info): fmt = self._get_struct_fmt(info) size = struct.calcsize(fmt) T = struct.unpack(fmt,f.read(size)) i = 0 for _, n, _ in info: setattr(self,n,T[i]) i = i + 1 def _dump(self,A): for a in A: print(a, getattr(self,a)) def _attr_names(self,*I): A = [] for i in I: if isinstance(i,str): A.append(i) else: A.extend([x[1] for x in i]) return A def _scanZTStr(self,f,loc): '''scan a zero terminated string from the file''' f.seek(loc) s = '' while 1: c = f.read(1) if c=='\000': break s = s+c return s def _scanN(self,N,fmt,f,loc): if not loc: return None fmt = len(fmt)==1 and ("<%d%c" % (N,fmt)) or ("<"+N*fmt) f.seek(loc) size = struct.calcsize(fmt) return struct.unpack(fmt,f.read(size)) def _scanNT(self,T,N,fmt,f,loc): if not loc: return None n = len(fmt) X = [] i = 0 S = [] for x in self._scanN(N,fmt,f,loc): S.append(x) i = i + 1 if i==n: X.append(S) i = 0 S = [] return list(map(lambda x,T=T: T(*x),X)) class KernPair: '''hold info about a possible kerning pair''' def __init__(self,first,second,amount): self.first = first self.scond = second self.amount = amount class KernTrack: def __init__(self,degree,minSize,minAmount,maxSize,maxAmount): ''' degree amount to change the character spacing. Negative values mean closer together,p ositive values mean farther apart. minSize minimum font height (in device units) for which to use linear track kerning. minAmount track kerning amount to use for font heights less or equal ktMinSize. maxSize maximum font height (in device units) for which to use linear track kerning.f For font heights between ktMinSize and ktMaxSize the track kerning amount has to increase linearily from ktMinAmount to ktMaxAmount. maxAmount track kerning amount to use for font heights greater or equal ktMaxSize. ''' self.degree = degree self.minSize = minSize self.minAmount = minAmount self.maxSize = maxSize self.maxAmount = maxAmount class PFM(_BUILDER): def __init__(self,fn=None): if fn: if isinstance(fn,str): f = open(fn,'rb') else: f = fn self.scan_from_file(f) if f is not fn: f.close() '''Class to hold information scanned from a type-1 .pfm file''' def scan_from_file(self,f): self._scan_from_file(f,self._header_struct_info) if self.dfType!=0x81: raise ValueError("Not a Type-1 Font description") else: self.WidthTable = None self._scan_from_file(f,self._extension_struct_info) if not self.dfExtentTable: raise ValueError('dfExtentTable is zero') if not self.dfExtMetricsOffset: raise ValueError('dfExtMetricsOffset is zero') if self.dfDevice: self.DeviceName = self._scanZTStr(f,self.dfDevice) else: self.DeviceName = None if self.dfFace: self.FaceName = self._scanZTStr(f,self.dfFace) else: self.FaceName = None f.seek(self.dfExtMetricsOffset) self._scan_from_file(f, self._extTextMetrics_struct_info) N = self.dfLastChar - self.dfFirstChar + 1 self.ExtentTable = self._scanN(N,'H',f,self.dfExtentTable) if self.dfDriverInfo: self.DriverInfo = self._scanZTStr(f,self.dfDriverInfo) else: self.DriverInfo = None if self.dfPairKernTable: self.KerningPairs = self._scanNT(KernPair,self.dfPairKernTable,'BBh',f,self.etmKernPairs) else: self.KerningPairs = [] if self.dfTrackKernTable: self.KerningTracks = self._scanNT(KernTrack,self.dfTrackKernTable,'hhhhh',f,self.etmKernTracks) else: self.KerningTracks = [] def dump(self): self._dump( self._attr_names( self._header_struct_info,'WidthTable', self._extension_struct_info, 'DeviceName', 'FaceName', self._extTextMetrics_struct_info, 'DriverInfo', )) _header_struct_info = (('H','dfVersion', '''This field contains the version of the PFM file. For PFM files that conform to this description (namely PFM files for Type-1 fonts) the value of this field is always 0x0100.'''), ('i','dfSize', '''This field contains the total size of the PFM file in bytes. Some drivers check this field and compare its value with the size of the PFM file, and if these two values don't match the font is ignored (I know this happens e.g. with Adobe PostScript printer drivers). '''), ('60s','dfCopyright', '''This field contains a null-terminated copyright string, often from the application that created the PFM file (this normally isn't the copyright string for the font file itself). The unused bytes in this field should be set to zero. '''), ('H','dfType', '''This field contains the font type. The low-order byte is a combination of the following values (only the values being of interest in PFM files are given): 0x00 (PF_RASTER_TYPE): font is a raster font 0x01 (PF_VECTOR_TYPE): font is a vector font 0x80 (PF_DEVICE_REALIZED): font realized by the device driver The high-order byte is never used in PFM files, it is always zero. In PFM files for Type-1 fonts the value in this field is always 0x0081. '''), ('H','dfPoints', '''This field contains the point size at which this font looks best. Since this is not relevant for scalable fonts the field is ignored. The value of this field should be set to 0x000a (10 pt). '''), ('H','dfVertRes', '''This field contains the vertical resolution at which the font was digitized (the value is in dots per inch). The value of this field should be set to 0x012C (300 dpi). '''), ('H','dfHorizRes', '''This field contains the horizontal resolution at which the font was digitized (the value is in dots per inch). The value of this field should be set to 0x012C (300 dpi). '''), ('H','dfAscent', '''This field contains the distance from the top of a character definition cell to the baseline of the typographical font. It is useful for aligning the baseline of fonts of different heights. '''), ('H','dfInternalLeading', '''This field contains the amount of leading inside the bounds set by the dfPixHeight field in the PFMHEADER structure. Accent marks may occur in this area. '''), ('H','dfExternalLeading', '''This field contains the amount of extra leading that the designer requests the application to add between rows. Since this area is outside the character definition cells, it contains no marks and will not be altered by text outputs. '''), ('B','dfItalic', '''This field specifies whether this font is an italic (or oblique) font. The low-order bit is 1 if the flag is set, all other bits are zero. '''), ('B','dfUnderline', '''This field specifies whether this font is an underlined font. The low-order bit is 1 if the flag is set, all other bits are zero. '''), ('B','dfStrikeOut', '''This field specifies whether this font is a striked-out font. The low-order bit is 1 if the flag is set, all other bits are zero. '''), ('H','dfWeight', '''This field contains the weight of the characters in this font. The value is on a scale from 0 through 1000, increments are in steps of 100 each. The values roughly give the number of black pixel from every 1000 pixels. Typical values are: 0 (FW_DONTCARE): unknown or no information 300 (FW_LIGHT): light font 400 (FW_NORMAL): normal font 700 (FW_BOLD): bold font '''), ('B','dfCharSet', '''This field specifies the character set used in this font. It can be one of the following values (probably other values may be used here as well): 0x00 (ANSI_CHARSET): the font uses the ANSI character set; this means that the font implements all characters needed for the current Windows code page (e.g. 1252). In case of a Type-1 font this font has been created with the encoding StandardEncoding Note that the code page number itself is not stored in the PFM file. 0x02 (SYMBOL_CHARSET): the font uses a font-specific encoding which will be used unchanged in displaying an printing text using this font. In case of a Type-1 font this font has been created with a font-specific encoding vector. Typical examples are the Symbol and the ZapfDingbats fonts. 0xFF (OEM_CHARSET): the font uses the OEM character set; this means that the font implements all characters needed for the code page 437 used in e.g. MS-DOS command line mode (at least in some versions of Windows, others might use code page 850 instead). In case of a Type-1 font this font has been created with a font-specific encoding vector. '''), ('H','dfPixWidth', '''This field contains the width of all characters in the font. For raster fonts this field contains the width in pixels of every character bitmap if the font is fixed-pitch, otherwise this field is zero and the character's widths are specified in the WidthTable table. For vector fonts this field contains the width of the grid on which the font was digitized. The value is ignored by PostScript printer drivers. '''), ('H','dfPixHeight', '''This field contains the height of all characters in the font. For raster fonts this field contains the height in scan lines of every character bitmap. For vector fonts this field contains the height of the grid on which the font was digitized. The value is ignored by PostScript printer drivers. '''), ('B','dfPitchAndFamily', '''This field specifies the font pitch and the font family. The font pitch specifies whether all characters in the font have the same pitch (this is called fixed pitch too) or variable pitch. The font family indicates, in a rather general way, the look of a font. The least significant bit in this field contains the pitch flag. If the bit is set the font is variable pitch, otherwise it's fixed pitch. For Type-1 fonts this flag is set always, even if the Type-1 font is fixed pitch. The most significant bits of this field specify the font family. These bits may have one of the following values: 0x00 (FF_DONTCARE): no information 0x10 (FF_ROMAN): serif font, variable pitch 0x20 (FF_SWISS): sans serif font, variable pitch 0x30 (FF_MODERN): fixed pitch, serif or sans serif font 0x40 (FF_SCRIPT): cursive or handwriting font 0x50 (FF_DECORATIVE): novelty fonts '''), ('H','dfAvgWidth', '''This field contains the average width of the characters in the font. For a fixed pitch font this is the same as dfPixWidth in the PFMHEADER structure. For a variable pitch font this is the width of the character 'X'. '''), ('H','dfMaxWidth', '''This field contains the maximum width of the characters in the font. For a fixed pitch font this value is identical to dfAvgWidth in the PFMHEADER structure. '''), ('B','dfFirstChar', '''This field specifies the first character code defined by this font. Width definitions are stored only for the characters actually present in a font, so this field must be used when calculating indexes into the WidthTable or the ExtentTable tables. For text fonts this field is normally set to 0x20 (character space). '''), ('B','dfLastChar', '''This field specifies the last character code defined by this font. Together with the dfFirstChar field in the PFMHEADER structure this field specifies the valid character range for this font. There must be an entry in the WidthTable or the ExtentTable tables for every character between these two values (including these values themselves). For text fonts this field is normally set to 0xFF (maximum possible value). '''), ('B','dfDefaultChar', '''This field specifies the default character to be used whenever a character is used that is outside the range of the dfFirstChar through dfLastChar fields in the PFMHEADER structure. The character is given relative to dfFirstChar so that the actual value of the default character is the sum of dfFirstChar and dfDefaultChar. Ideally, the default character should be a visible character in the current font, e.g. a period ('.'). For text fonts this field is normally set to either 0x00 (character space) or 0x75 (bullet). '''), ('B','dfBreakChar', '''This field specifies the word-break character. Applications use this character to separate words when wrapping or justifying lines of text. The character is given relative to dfFirstChar in the PFMHEADER structure so that the actual value of the word-break character is the sum of dfFirstChar and dfBreakChar. For text fonts this field is normally set to 0x00 (character space). '''), ('H','dfWidthBytes', '''This field contains the number of bytes in every row of the font bitmap. The value is always an even quantity so that rows of the bitmap start on 16 bit boundaries. This field is not used for vector fonts, it is therefore zero in e.g. PFM files for Type-1 fonts. '''), ('i','dfDevice', '''This field contains the offset from the beginning of the PFM file to the DeviceName character buffer. The DeviceName is always present in PFM files for Type-1 fonts, this field is therefore never zero.'''), ('i','dfFace', '''This field contains the offset from the beginning of the PFM file to the FaceName character buffer. The FaceName is always present in PFM files for Type-1 fonts, this field is therefore never zero. '''), ('i','dfBitsPointer', '''This field is not used in PFM files, it must be set to zero. '''), ('i','dfBitsOffset', '''This field is not used in PFM files, it must be set to zero. '''), ) #'H','WidthTable[]' #This section is present in a PFM file only when this PFM file describes a #variable pitch raster font. Since Type-1 fonts aren't raster fonts this #section never exists in PFM files for Type-1 fonts.''' #The WidthTable table consists of (dfLastChar - dfFirstChar + 2) entries of type WORD (dfFirstChar and dfLastChar can be found in the #PFMHEADER structure). Every entry contains the width of the corresponding character, the last entry in this table is extra, it is set to zero. _extension_struct_info=( ('H','dfSizeFields', '''This field contains the size (in bytes) of the PFMEXTENSION structure. The value is always 0x001e. '''), ('I','dfExtMetricsOffset', '''This field contains the offset from the beginning of the PFM file to the ExtTextMetrics section. The ExtTextMetrics section is always present in PFM files for Type-1 fonts, this field is therefore never zero. '''), ('I','dfExtentTable', '''This field contains the offset from the beginning of the PFM file to the ExtentTable table. This table is always present in PFM files for Type-1 fonts, this field is therefore never zero. '''), ('I','dfOriginTable', '''This field contains the offset from the beginning of the PFM file to a table containing origin coordinates for screen fonts. This table is not present in PFM files for Type-1 fonts, the field must therefore be set to zero. '''), ('I','dfPairKernTable', '''This field contains the offset from the beginning of the PFM file to the KerningPairs table. The value must be zero if the PFM file doesn't contain a KerningPairs table. '''), ('I','dfTrackKernTable', '''This field contains the offset from the beginning of the PFM file to the KerningTracks table. The value must be zero if the PFM file doesn't contain a kerningTracks table. '''), ('I','dfDriverInfo', '''This field contains the offset from the beginning of the PFM file to the DriverInfo section. This section is always present in PFM files for Type-1 fonts, this field is therefore never zero. '''), ('I','dfReserved', '''This field must be set to zero. '''), ) #char DeviceName[] #The DeviceName character buffer is a null-terminated string #containing the name of the printer driver family. PFM files #for Type-1 fonts have the string 'PostScript', PFM files for #PCL fonts have the string 'PCL/HP LaserJet'. #char FaceName[] #The FaceName character buffer is a null-terminated string #containing the name of the font face. In PFM files for Type-1 #fonts this is normally #the PostScript name of the font without suffixes like #'-Bold', '-Italic' etc. _extTextMetrics_struct_info = (('h','etmSize', '''This field contains the size (in bytes) of the EXTTEXTMETRIC structure. The value is always 0x0034. '''), ('h','etmPointSize', '''This field contains the nominal point size of the font in twips (this is a twentieth of a point or 1/1440 inch). This is the intended graphics art size of the font, the actual size may differ slightly depending on the resolution of the output device. In PFM files for Type-1 fonts this value should be set to 0x00f0 (240 twips or 12 pt). '''), ('h','etmOrientation', '''This field contains the orientation of the font. This value refers to the ability of the font to be imaged on a page of a given orientation. It can be one of the following values: 0x0000: any orientation 0x0001: portrait (page width is smaller that its height) 0x0002: landscape (page width is greater than its height) In PFM files for Type-1 fonts this field is always 0x0000 since a Type-1 font can be arbitrarily rotated. '''), ('h','etmMasterHeight', '''This field contains the font size in device units for which the values in the ExtentTable table are exact. Since Type-1 fonts are by convention defined in a box of 1000 x 1000 units, PFM files for Type-1 fonts have the value 0x03E8 (1000, the number of units per em) in this field. '''), ('h','etmMinScale', '''This field contains the minimum valid size for the font in device units. The minimum valid point size can then be calculated as follows: (etmMinScale * points-per-inch) / dfVertRes The value for 'points-per-inch' is normally 72, the dfVertRes field can be found in the PFMHEADER structure, it contains the vertical resolution at which the font was digitized (this value is in dots per inch). In PFM files for Type-1 fonts the value should be set to 0x0003. '''), ('h','etmMaxScale', '''This field contains the maximum valid size for the font in device units. The maximum valid point size can then be calculated as follows: (etmMaxScale * points-per-inch) / dfVertRes (see also above etmMinScale). In PFM files for Type-1 fonts the value should be set to 0x03E8 (1000). '''), ('h','etmMasterUnits', '''This field contains the integer number of units per em where an em equals etmMasterHeight in the EXTTEXTMETRIC structure. In other words, the etmMasterHeight value is expressed in font units rather than device units. In PFM files for Type-1 fonts the value should be set to 0x03E8 (1000). '''), ('h','etmCapHeight', '''This field contains the height for uppercase characters in the font (the value is in font units). Typically, the character 'H' is used for measurement purposes. For Type-1 fonts you may find this value in the AFM file. '''), ('h','etmXHeight', '''This field contains the height for lowercase characters in the font (the value is in font units). Typically, the character 'x' is used for measurement purposes. For Type-1 fonts you may find this value in the AFM file. '''), ('h','etmLowerCaseAscent', '''This field contains the distance (in font units) that the ascender of lowercase letters extends above the baseline. This distance is typically specified for a lowercase character 'd'. For Type-1 fonts you may find this value in the AFM file. '''), ('h','etmLowerCaseDescent', '''This field contains the distance (in font units) that the descender of lowercase letters extends below the baseline. This distance is typically specified for a lowercase character 'p'. For Type-1 fonts you may find this value in the AFM file. '''), ('h','etmSlant', '''This field contains the angle in tenth of degrees clockwise from the upright version of the font. The value is typically not zero only for an italic or oblique font. For Type-1 fonts you may find this value in the AFM file (search for the entry 'ItalicAngle' and multiply it by 10). '''), ('h','etmSuperScript', '''This field contains the recommended amount (in font units) to offset superscript characters from the baseline. This amount is typically specified by a negative offset. '''), ('h','etmSubScript', '''This field contains the recommended amount (in font units) to offset subscript characters from the baseline. This amount is typically specified by a positive offset. '''), ('h','etmSuperScriptSize', '''This field contains the recommended size (in font units) for superscript characters in the font. '''), ('h','etmSubScriptSize', '''This field contains the recommended size (in font units) for subscript characters in the font. '''), ('h','etmUnderlineOffset', '''This field contains the offset (in font units) downward from the baseline where the top of a single underline bar should appear. For Type-1 fonts you may find this value in the AFM file. '''), ('h','etmUnderlineWidth', '''This field contains the thickness (in font units) of the underline bar. For Type-1 fonts you may find this value in the AFM file. '''), ('h','etmDoubleUpperUnderlineOffset', '''This field contains the offset (in font units) downward from the baseline where the top of the upper, double underline bar should appear. '''), ('h','etmDoubleLowerUnderlineOffset', '''This field contains the offset (in font units) downward from the baseline where the top of the lower, double underline bar should appear. '''), ('h','etmDoubleUpperUnderlineWidth', '''This field contains the thickness (in font units) of the upper, double underline bar. '''), ('h','etmDoubleLowerUnderlineWidth', '''This field contains the thickness (in font units) of the lower, double underline bar. '''), ('h','etmStrikeOutOffset', '''This field contains the offset (in font units) upward from the baseline where the top of a strikeout bar should appear. '''), ('h','etmStrikeOutWidth', '''This field contains the thickness (in font units) of the strikeout bar. '''), ('H','etmKernPairs', '''This field contains the number of kerning pairs defined in the KerningPairs table in this PFM file. The number (and therefore the table) may not be greater than 512. If the PFM file doesn't contain a KerningPairs table the value is zero. '''), ('H','etmKernTracks', '''This field contains the number of kerning tracks defined in the KerningTracks table in this PFM file. The number (and therefore the table) may not be greater than 16. If the PFM file doesn't contain a KerningTracks table the value is zero. '''), ) #'H','ExtentTable[]' #The ExtentTable table must be present in a PFM file for a Type-1 font, #it contains the unscaled widths (in 1/1000's of an em) of the characters #in the font. The table consists of (dfLastChar - dfFirstChar + 1) entries #of type WORD (dfFirstChar and dfLastChar can be found in the PFMHEADER #structure). For Type-1 fonts these widths can be found in the AFM file. #DRIVERINFO DriverInfo #The DriverInfo section must be present in a PFM file for a Type-1 font, #in this case it consists of a null-terminated string containing the #PostScript name of the font. #PAIRKERN KerningPairs[] #The KerningPairs table need not be present in a PFM file for a Type-1 #font, if it exists it contains etmKernPairs (from the EXTTEXTMETRIC #structure) entries. Each of these entries looks as follows: #B kpFirst This field contains the first (left) character of the kerning pair. #B kpSecond This field contains the second (right) character of the kerning pair. #h kpKernAmount This field contains the kerning amount in font units, the value # is mostly negative. #KERNTRACK KerningTracks[] #The KerningTracks table need not be present in a PFM file for a Type-1 font, if it exists it contains etmKernTracks (from the EXTTEXTMETRIC structure) entries. Each of these entries looks as follows: #h ktDegree This field contains the amount to change the character spacing. Negative values mean closer together, positive values mean farther apart. #h ktMinSize This field contains the minimum font height (in device units) for which to use linear track kerning. #h ktMinAmount This field contains the track kerning amount to use for font heights less or equal ktMinSize. #h ktMaxSize This field contains the maximum font height (in device units) for which to use linear track kerning. For font heights between ktMinSize and ktMaxSize the track kerning amount has to increase linearily from ktMinAmount to ktMaxAmount. #h ktMaxAmount This field contains the track kerning amount to use for font heights greater or equal ktMaxSize. if __name__=='__main__': from glob import glob for f in glob('/Program Files/Adobe/Acrobat 4.0/resource/font/pfm/*.pfm'): print(f) p=PFM(f) p.dump() rl-renderpm-4.0.3/src/test_renderPM.py000066400000000000000000000421721453236046100176730ustar00rootroot00000000000000if __name__=='__main__': import sys, os, traceback import _renderPM from reportlab.graphics import shapes, renderPM from reportlab.rl_config import verbose def test_base(): class dummy: pass g=_renderPM.gstate(1,1) try: g.aaa = 3 print('Wrong handling of bad attribute') except AttributeError: if verbose: print('bad attribute handled ok') for fontName, fontSize in (('aaa',10),('Times-Roman','10'),('Times-Roman',-10),(1,10)): try: g.setFont(fontName,fontSize) print('Wrong handling of setFont(%s,%s)' % (fontName,fontSize)) except _renderPM.Error: if verbose: print('_renderPM.error detected OK') except TypeError: if verbose: print('Type detected OK') for a in ('strokeColor','fillColor'): try: setattr(g,a,(1,2,3)) print('Wrong handling of bad '+a) except ValueError: if verbose: print('wrong handling of bad %s detected OK' % a) try: c=dummy() c.red=0xff/255. c.green=0xaf/255. c.blue=0xbf/255. for v,r in ((None,None),(0xfffafb,0xfffafb),(c,0xffafbf)): setattr(g,a,v) assert getattr(g,a)==r, "%s should be %s" % (a,hex(r)) if verbose: print('setattr(%s) OK' % a) except: print('wrong handling of good %s' % a) traceback.print_exc() print(hex(getattr(g,a))) for v in ('a',1,(1,'a'),('a',1),(1,()),(1,('a',2))): try: g.dashArray=v print('Wrong handling of dashArray %s' % v) except ValueError: if verbose: print('Wrong handling of dashArray %s detected OK' % v) try: g.dashArray=7,(1,2,3) assert g.dashArray==(7.0,(1.0,2.0,3.0)), "should be (7.0,(1.0,2.0,3.0))" if verbose: print('dashArray obtained OK') except: print('wrong handling of dashArray') traceback.print_exc() print(g.dashArray) try: g.pathBegin() g.moveTo(0,0) g.lineTo(1,0) g.lineTo(1,1) g.lineTo(0,1) g.pathClose() good = (('moveToClosed', 0.0, 0.0), ('lineTo', 1.0, 0.0), ('lineTo', 1.0, 1.0), ('lineTo', 0.0, 1.0), ('lineTo', 0.0, 0.0)) assert good==g.path, 'Wrong path should be %s' % str(good) if verbose: print('path attribute obtained OK') except: print('wrong handling of path') traceback.print_exc() print(g.path) if len(sys.argv)==1: test_base() else: def do_save(c,n,txt=0,pil=0): DIR='pmout' if not os.path.isdir(DIR): os.mkdir(DIR) c.saveToFile(os.path.join(DIR,"test_renderPM_%03d.gif"%n)) if txt: f = open(os.path.join(DIR,"test_renderPM_%03d.txt"%n),'w') b = c.pixBuf w = c.width h = c.height k = 0 for i in range(h): f.write('%6.6x: '% i ); for j in range(w): v = (ord(b[k])<<16) | (ord(b[k+1])<<8) | ord(b[k+2]) k = k + 3 f.write(' %6.6x'%v) f.write('\n') if pil: from PIL import Image im = Image.new('RGB', size=(w, h)) im.fromstring(b) f.write('PIL\n') for i in range(h): f.write('%6.6x: '% i ); for j in range(w): v = im.getpixel((i,j)) f.write(' %2.2x%2.2x%2.2x'%v) f.write('\n') im.save(os.path.join(DIR,"test_renderPM_%03dx.jpg"%n),'JPEG') im.save(os.path.join(DIR,"test_renderPM_%03dx.bmp"%n),'BMP') im.save(os.path.join(DIR,"test_renderPM_%03dx.tif"%n),'TIFF') im = im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE) im.save(os.path.join(DIR,"test_renderPM_%03dx.gif"%n),'GIF') def flagged(n): return str(n) in sys.argv or 'all' in sys.argv def doVPath(c, S,x0=0,y0=0): c.pathBegin() for P in S: c.moveTo(P[0][0]-x0,P[0][1]-y0) for p in P[1:]: c.lineTo(p[0]-x0,p[1]-y0) c.pathClose() c.pathFill() def doCTest(f, c, x, y, c0=0x8000,c1=0xff0000): c.ctm=(1,0,0,1,x,y) c.fillColor = c0 c.strokeColor = c1 f(c) if flagged(0): vp=[[(136.262,131.996), (136.91,130.502), (137.038,130.192), (137.18,129.832), (137.243,129.646), (137.295,129.464), (137.329,129.292), (137.342,129.134), (137.337,129.021), (137.321,128.92), (137.295,128.828), (137.26,128.747), (137.214,128.674), (137.159,128.61), (137.094,128.555), (137.02,128.506), (136.937,128.465), (136.845,128.43), (136.745,128.401), (136.636,128.377), (136.393,128.343), (136.118,128.324), (136.118,128), (140.816,128), (140.816,128.324), (140.578,128.345), (140.361,128.387), (140.164,128.449), (139.985,128.53), (139.824,128.627), (139.677,128.741), (139.544,128.869), (139.423,129.01), (139.313,129.164), (139.211,129.328), (139.116,129.502), (139.026,129.683), (138.858,130.066), (138.692,130.466), (134.642,140.186), (134.318,140.186), (130.034,130.142), (129.838,129.706), (129.743,129.514), (129.648,129.339), (129.553,129.18), (129.457,129.037), (129.358,128.908), (129.256,128.794), (129.148,128.694), (129.036,128.607), (128.916,128.532), (128.789,128.469), (128.652,128.417), (128.506,128.376), (128.349,128.345), (128.18,128.324), (128.18,128), (131.924,128), (131.924,128.324), (131.673,128.346), (131.426,128.377), (131.193,128.423), (131.084,128.453), (130.981,128.491), (130.887,128.535), (130.801,128.587), (130.725,128.648), (130.661,128.719), (130.609,128.8), (130.57,128.893), (130.546,128.998), (130.538,129.116), (130.549,129.253), (130.58,129.406), (130.625,129.567), (130.682,129.732), (130.811,130.053), (130.934,130.322), (131.654,131.996), (136.262,131.996)], [(136.028,132.644), (131.924,132.644), (133.994,137.45), (136.028,132.644)]] def doTest0(c, vp, x, y, c0=0x0,c1=0xff0000, angle=0, c2=0xff00ff): c.ctm=(1,0,0,1,x,y) c.fillColor = c0 doVPath(c,vp,128,128) c.ctm = shapes.mmult(c.ctm, shapes.rotate(180)) c.fillColor = c1 doVPath(c,vp,128,128) c.ctm=(1,0,0,1,x,y) c.pathBegin() c.ctm=(1,0,0,1,x+20,y) c.ctm = shapes.mmult(c.ctm, shapes.rotate(angle)) c.moveTo(0,0) c.lineTo(20,0) c.strokeColor = c2 c.pathStroke() c.ctm=(1,0,0,1,x+20,y-20) c.drawString(0,0,"Robin") c = renderPM.PMCanvas(256, 256, bg=0xffffff) c.fillColor = 0x000000 c.setFont('Times-Roman',18) doTest0(c,vp, 128, 128 ) vp[0].reverse() vp[1].reverse() doTest0(c,vp, 168, 168, c0=0xff00, c1=0xff, angle=45) do_save(c,0) def doCPath1(c): c.pathBegin() c.moveTo(110-85,100-85) c.curveTo(110-85,94.477152501999996-85, 105.522847498-85,90-85, 100-85,90-85) c.curveTo(94.477152501999996-85,90-85, 90-85,94.477152501999996-85, 90-85,100-85) c.curveTo(90-85,105.522847498-85, 94.477152501999996-85,110-85, 100-85,110-85) c.curveTo(105.522847498-85,110-85, 110-85,105.522847498-85, 110-85,100-85) c.pathClose() c.pathFill() c.pathStroke() def doCPath2(c): c.pathBegin() c.moveTo(5,5) c.lineTo(5,20) c.lineTo(20,20) c.lineTo(20,5) c.pathClose() c.pathFill() def rotate_alpha_blend_text(can,off_x, text, dw, n, end_alpha): "decrease alpha linearly over the range of n points" dalpha = end_alpha/n for ii in range(n): can.gsave() can.rotate(dw*ii) # print dw*ii can.gstate.fill_opacity = end_alpha-ii*dalpha print("alpha = ", can.gstate.fill_opacity) can.drawString(off_x,0, text) can.grestore() def doCPath4(c,doStroke=1,doFill=1): c.pathBegin() c.moveTo(5,5) c.lineTo(5,20) c.lineTo(20,20) c.lineTo(20,5) c.pathClose() if doFill: c.pathFill() if doStroke: c.pathStroke() def doCPath5(c,doStroke=1): c.pathBegin() c.moveTo(5,5) c.lineTo(5,20) c.lineTo(20,20) c.lineTo(20,5) c.pathClose() if doStroke: c.pathStroke() def doCPath6(c,doStroke=1): c.pathBegin() c.moveTo(5,10) c.lineTo(5,20) c.lineTo(20,20) c.lineTo(20,10) c.pathClose() if doStroke: c.pathStroke() if flagged(1): c = renderPM.PMCanvas(25, 25, bg=0xffffff) c.setFont('Times-Roman',18) doCTest(doCPath1, c, 0, 0 ) do_save(c,1) if flagged(2): c = renderPM.PMCanvas(25, 25, bg=0xffffff) doCTest(doCPath2, c, 0, 0 ) do_save(c,2,txt=1,pil=1) if flagged(3): c = renderPM.PMCanvas(256, 256, bg=0xffffff) c.fillColor = 0x000000 c.setFont('Times-Roman',18) text = "ABC" c.ctm=(1,0,0,("invert" in sys.argv) and -1 or 1, 127.5,127.5) c.drawString(0, 0, text) c.ctm = shapes.mmult(c.ctm, shapes.rotate(180)) c.fillColor = 0xff0000 c.drawString(0, 0, text) do_save(c,3) if flagged(4): c = renderPM.PMCanvas(25, 25, bg=0xffffff) doCTest(doCPath4, c, 0, 0, c0=0x8000, c1=0xff0000) do_save(c,4) if flagged(5): c = renderPM.PMCanvas(25, 25, bg=0xffffff) doCTest(doCPath5, c, 0, 0 ) do_save(c,5) if flagged(6): c = renderPM.PMCanvas(250, 250, bg=0xffffff) c.pathBegin() c.moveTo(50,50) c.lineTo(50,200) c.lineTo(200,200) c.lineTo(200,50) c.pathClose() c.clipPathSet() c.pathBegin() c.moveTo(100,20) c.lineTo(100,220) c.lineTo(180,220) c.lineTo(180,20) c.pathClose() c.strokeColor = 0xff0000 c.fillColor = 0x00ff00 c.pathFill() #c.pathStroke() c.clipPathClear() do_save(c,6) if flagged(7): p=[('moveTo', 4.0, 0.0), ('lineTo', 4.0, 35.800000000000004), ('lineTo', 19.850000000000001, 35.800000000000004), ('curveTo', 22.350000000000001, 35.800000000000004, 24.650000000000002, 1.2325000000000002, 27.0, 34.850000000000001), ('curveTo', 30.650000000000002, 33.450000000000003, 32.600000000000001, 1.6300000000000001, 32.600000000000001, 26.0), ('curveTo', 32.600000000000001, 20.25, 28.650000000000002, 1.4325000000000001, 22.550000000000001, 16.350000000000001), ('lineTo', 22.550000000000001, 16.25), ('curveTo', 24.350000000000001, 15.4, 26.5, 1.3250000000000002, 29.300000000000001, 9.75), ('lineTo', 35.550000000000004, 0.0), ('lineTo', 29.600000000000001, 0.0), ('lineTo', 24.900000000000002, 7.4000000000000004), ('curveTo', 19.800000000000001, 15.450000000000001, 18.150000000000002, 0.9075000000000002, 14.200000000000001, 15.800000000000001), ('lineTo', 8.75, 15.800000000000001), ('lineTo', 8.75, 0.0), ('lineTo', 4.0, 0.0), ('closePath',), ('moveTo', 57.300000000000004, 8.3499999999999996), ('lineTo', 61.800000000000004, 7.8000000000000007), ('curveTo', 60.5, 2.9000000000000004, 56.850000000000001, 2.8425000000000002, 50.450000000000003, -0.60000000000000009), ('curveTo', 42.400000000000006, -0.60000000000000009, 38.050000000000004, 1.9025000000000003, 38.050000000000004, 12.75), ('curveTo', 38.050000000000004, 20.650000000000002, 42.300000000000004, 2.1150000000000002, 50.200000000000003, 26.5), ('curveTo', 52.400000000000006, 26.5, 62.350000000000001, 3.1175000000000002, 61.950000000000003, 11.850000000000001), ('lineTo', 42.550000000000004, 11.850000000000001), ('curveTo', 42.900000000000006, 5.6500000000000004, 46.550000000000004, 2.3275000000000001, 50.450000000000003, 3.0), ('curveTo', 55.400000000000006, 3.0, 56.75, 2.8375000000000004, 57.300000000000004, 8.3499999999999996), ('closePath',), ('moveTo', 67.300000000000011, -9.9500000000000011), ('lineTo', 67.300000000000011, 25.900000000000002), ('lineTo', 71.300000000000011, 25.900000000000002), ('lineTo', 71.300000000000011, 22.550000000000001), ('lineTo', 71.400000000000006, 22.550000000000001), ('curveTo', 72.150000000000006, 23.75, 74.100000000000009, 3.7050000000000005, 78.800000000000011, 26.5), ('curveTo', 86.150000000000006, 26.5, 89.800000000000011, 4.4900000000000011, 89.800000000000011, 13.15), ('curveTo', 89.800000000000011, 3.9500000000000002, 84.200000000000003, 4.21, 78.5, -0.60000000000000009), ('curveTo', 75.400000000000006, -0.60000000000000009, 73.100000000000009, 3.6550000000000007, 71.800000000000011, 2.7000000000000002), ('lineTo', 71.700000000000003, 2.7000000000000002), ('lineTo', 71.700000000000003, -9.9500000000000011), ('lineTo', 67.300000000000011, -9.9500000000000011), ('closePath',), ('moveTo', 93.450000000000003, 12.950000000000001), ('curveTo', 93.450000000000003, 15.75, 93.900000000000006, 4.6950000000000003, 96.100000000000009, 22.200000000000003), ('curveTo', 98.450000000000003, 25.200000000000003, 102.2, 5.1100000000000003, 105.60000000000001, 26.5), ('curveTo', 111.05000000000001, 26.5, 117.75, 5.8875000000000002, 117.75, 13.350000000000001), ('curveTo', 117.75, 7.8000000000000007, 116.35000000000001, 5.8175000000000008, 115.15000000000001, 3.8000000000000003), ('curveTo', 112.7, 0.80000000000000004, 109.05000000000001, 5.4525000000000006, 105.60000000000001, -0.60000000000000009), ('curveTo', 102.10000000000001, -0.60000000000000009, 98.450000000000003, 4.9225000000000003, 96.0, 3.8000000000000003), ('curveTo', 93.600000000000009, 6.8000000000000007, 93.450000000000003, 4.6725000000000003, 93.450000000000003, 12.950000000000001), ('closePath',), ('moveTo', 184.80000000000001, 17.949999999999999), ('lineTo', 180.50000000000003, 18.5), ('curveTo', 181.00000000000003, 20.800000000000001, 182.20000000000002, 9.1100000000000012, 192.20000000000002, 26.5), ('curveTo', 202.25000000000003, 26.5, 202.25000000000003, 10.112500000000002, 202.25000000000003, 16.75), ('lineTo', 202.25000000000003, 10.850000000000001), ('curveTo', 202.25000000000003, 4.2000000000000002, 202.25000000000003, 10.112500000000002, 203.65000000000003, 0.0), ('lineTo', 199.05000000000001, 0.0), ('curveTo', 198.35000000000002, 1.3500000000000001, 198.25000000000003, 9.9125000000000014, 198.15000000000003, 3.2000000000000002), ('curveTo', 194.45000000000002, 0.050000000000000003, 191.40000000000003, 9.5700000000000021, 188.60000000000002, -0.60000000000000009), ('curveTo', 182.90000000000003, -0.60000000000000009, 179.75000000000003, 8.9875000000000025, 179.75000000000003, 6.8500000000000005), ('curveTo', 179.75000000000003, 13.75, 187.10000000000002, 9.3550000000000022, 189.95000000000002, 14.950000000000001), ('curveTo', 190.60000000000002, 15.050000000000001, 195.35000000000002, 9.7675000000000018, 197.85000000000002, 16.449999999999999), ('curveTo', 197.95000000000002, 19.25, 198.00000000000003, 9.9000000000000021, 191.55000000000001, 22.900000000000002), ('curveTo', 186.05000000000001, 22.900000000000002, 185.35000000000002, 9.2675000000000018, 184.80000000000001, 17.949999999999999), ('closePath',), ('moveTo', 213.10000000000002, 0.0), ('lineTo', 209.00000000000003, 0.0), ('lineTo', 209.00000000000003, 35.800000000000004), ('lineTo', 213.40000000000003, 35.800000000000004), ('lineTo', 213.40000000000003, 23.0), ('lineTo', 213.50000000000003, 23.0), ('curveTo', 214.10000000000002, 23.900000000000002, 216.20000000000005, 10.810000000000002, 220.50000000000003, 26.5), ('curveTo', 227.50000000000003, 26.5, 231.50000000000003, 11.575000000000003, 231.50000000000003, 13.350000000000001), ('curveTo', 231.50000000000003, 2.4000000000000004, 224.45000000000005, 11.222500000000004, 220.30000000000004, -0.60000000000000009), ('curveTo', 219.20000000000005, -0.60000000000000009, 215.60000000000002, 10.780000000000001, 213.20000000000005, 3.25), ('lineTo', 213.10000000000002, 3.25), ('lineTo', 213.10000000000002, 0.0), ('closePath',)] c = renderPM.PMCanvas(400, 200, bg=0xffffff) c.strokeWidth = 0 c.strokeColor = 0xff0000 c.fillColor = 0x00ff00 c.pathStroke() c.pathFill() do_save(c,7) rl-renderpm-4.0.3/src/tr.py000066400000000000000000000011131453236046100155330ustar00rootroot00000000000000from reportlab.rl_config import T1SearchPath T1SearchPath.insert(0,'C:\\Python\\PyArt\\pdffonts') import _renderPM from time import time from reportlab.pdfbase.pdfmetrics import getFont from reportlab.pdfbase._fontdata import standardFonts g = _renderPM.gstate(200,200) g.ctm = (1,0,0,1,0,0) N=20 t0 = time() for i in range(N): for k in standardFonts: f = getFont(k) print(k, f.face.findT1File()) _renderPM.makeT1Font(k,f.face.findT1File(),f.encoding.vector) _renderPM.delCache() print('Reading %d standard fonts %d times took %.2f seconds' % (len(standardFonts),N,time()-t0))